// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package url parses URLs and implements query escaping.
//
// See RFC 3986. This package generally follows RFC 3986, except where
// it deviates for compatibility reasons.
// RFC 6874 followed for IPv6 zone literals.

//go:generate go run gen_encoding_table.go

package url

// When sending changes, first  search old issues for history on decisions.
// Unit tests should also contain references to issue numbers with details.

import (
	
	
	
	
	
	
	
	
	_  // for linkname
)

var urlstrictcolons = godebug.New("urlstrictcolons")

// Error reports an error and the operation and URL that caused it.
type Error struct {
	Op  string
	URL string
	Err error
}

func ( *Error) () error { return .Err }
func ( *Error) () string { return fmt.Sprintf("%s %q: %s", .Op, .URL, .Err) }

func ( *Error) () bool {
	,  := .Err.(interface {
		() bool
	})
	return  && .()
}

func ( *Error) () bool {
	,  := .Err.(interface {
		() bool
	})
	return  && .()
}

const upperhex = "0123456789ABCDEF"

func ishex( byte) bool {
	return table[]&hexChar != 0
}

// Precondition: ishex(c) is true.
func unhex( byte) byte {
	return 9*(>>6) + ( & 15)
}

type EscapeError string

func ( EscapeError) () string {
	return "invalid URL escape " + strconv.Quote(string())
}

type InvalidHostError string

func ( InvalidHostError) () string {
	return "invalid character " + strconv.Quote(string()) + " in host name"
}

// See the reference implementation in gen_encoding_table.go.
func shouldEscape( byte,  encoding) bool {
	return table[]& == 0
}

// QueryUnescape does the inverse transformation of [QueryEscape],
// converting each 3-byte encoded substring of the form "%AB" into the
// hex-decoded byte 0xAB.
// It returns an error if any % is not followed by two hexadecimal
// digits.
func ( string) (string, error) {
	return unescape(, encodeQueryComponent)
}

// PathUnescape does the inverse transformation of [PathEscape],
// converting each 3-byte encoded substring of the form "%AB" into the
// hex-decoded byte 0xAB. It returns an error if any % is not followed
// by two hexadecimal digits.
//
// PathUnescape is identical to [QueryUnescape] except that it does not
// unescape '+' to ' ' (space).
func ( string) (string, error) {
	return unescape(, encodePathSegment)
}

// unescape unescapes a string; the mode specifies
// which section of the URL string is being unescaped.
func unescape( string,  encoding) (string, error) {
	// Count %, check that they're well-formed.
	 := 0
	 := false
	for  := 0;  < len(); {
		switch [] {
		case '%':
			++
			if +2 >= len() || !ishex([+1]) || !ishex([+2]) {
				 = [:]
				if len() > 3 {
					 = [:3]
				}
				return "", EscapeError()
			}
			// Per https://tools.ietf.org/html/rfc3986#page-21
			// in the host component %-encoding can only be used
			// for non-ASCII bytes.
			// But https://tools.ietf.org/html/rfc6874#section-2
			// introduces %25 being allowed to escape a percent sign
			// in IPv6 scoped-address literals. Yay.
			if  == encodeHost && unhex([+1]) < 8 && [:+3] != "%25" {
				return "", EscapeError([ : +3])
			}
			if  == encodeZone {
				// RFC 6874 says basically "anything goes" for zone identifiers
				// and that even non-ASCII can be redundantly escaped,
				// but it seems prudent to restrict %-escaped bytes here to those
				// that are valid host name bytes in their unescaped form.
				// That is, you can use escaping in the zone identifier but not
				// to introduce bytes you couldn't just write directly.
				// But Windows puts spaces here! Yay.
				 := unhex([+1])<<4 | unhex([+2])
				if [:+3] != "%25" &&  != ' ' && shouldEscape(, encodeHost) {
					return "", EscapeError([ : +3])
				}
			}
			 += 3
		case '+':
			 =  == encodeQueryComponent
			++
		default:
			if ( == encodeHost ||  == encodeZone) && [] < 0x80 && shouldEscape([], ) {
				return "", InvalidHostError([ : +1])
			}
			++
		}
	}

	if  == 0 && ! {
		return , nil
	}

	var  byte
	switch  {
	case encodeQueryComponent:
		 = ' '
	default:
		 = '+'
	}
	var  strings.Builder
	.Grow(len() - 2*)
	for  := 0;  < len(); ++ {
		switch [] {
		case '%':
			// In the loop above, we established that unhex's precondition is
			// fulfilled for both s[i+1] and s[i+2].
			.WriteByte(unhex([+1])<<4 | unhex([+2]))
			 += 2
		case '+':
			.WriteByte()
		default:
			.WriteByte([])
		}
	}
	return .String(), nil
}

// QueryEscape escapes the string so it can be safely placed
// inside a [URL] query.
func ( string) string {
	return escape(, encodeQueryComponent)
}

// PathEscape escapes the string so it can be safely placed inside a [URL] path segment,
// replacing special characters (including /) with %XX sequences as needed.
func ( string) string {
	return escape(, encodePathSegment)
}

func escape( string,  encoding) string {
	,  := 0, 0
	for ,  := range []byte() {
		if shouldEscape(, ) {
			if  == ' ' &&  == encodeQueryComponent {
				++
			} else {
				++
			}
		}
	}

	if  == 0 &&  == 0 {
		return 
	}

	var  [64]byte
	var  []byte

	 := len() + 2*
	if  <= len() {
		 = [:]
	} else {
		 = make([]byte, )
	}

	if  == 0 {
		copy(, )
		for  := 0;  < len(); ++ {
			if [] == ' ' {
				[] = '+'
			}
		}
		return string()
	}

	 := 0
	for ,  := range []byte() {
		switch {
		case  == ' ' &&  == encodeQueryComponent:
			[] = '+'
			++
		case shouldEscape(, ):
			[] = '%'
			[+1] = upperhex[>>4]
			[+2] = upperhex[&15]
			 += 3
		default:
			[] = 
			++
		}
	}
	return string()
}

// A URL represents a parsed URL (technically, a URI reference).
//
// The general form represented is:
//
//	[scheme:][//[userinfo@]host][/]path[?query][#fragment]
//
// URLs that do not start with a slash after the scheme are interpreted as:
//
//	scheme:opaque[?query][#fragment]
//
// The Host field contains the host and port subcomponents of the URL.
// When the port is present, it is separated from the host with a colon.
// When the host is an IPv6 address, it must be enclosed in square brackets:
// "[fe80::1]:80". The [net.JoinHostPort] function combines a host and port
// into a string suitable for the Host field, adding square brackets to
// the host when necessary.
//
// Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.
// A consequence is that it is impossible to tell which slashes in the Path were
// slashes in the raw URL and which were %2f. This distinction is rarely important,
// but when it is, the code should use the [URL.EscapedPath] method, which preserves
// the original encoding of Path. The Fragment field is also stored in decoded form,
// use [URL.EscapedFragment] to retrieve the original encoding.
//
// The [URL.String] method uses the [URL.EscapedPath] method to obtain the path.
type URL struct {
	Scheme   string
	Opaque   string    // encoded opaque data
	User     *Userinfo // username and password information
	Host     string    // "host" or "host:port" (see Hostname and Port methods)
	Path     string    // path (relative paths may omit leading slash)
	Fragment string    // fragment for references (without '#')

	// RawQuery contains the encoded query values, without the initial '?'.
	// Use URL.Query to decode the query.
	RawQuery string

	// RawPath is an optional field containing an encoded path hint.
	// See the EscapedPath method for more details.
	//
	// In general, code should call EscapedPath instead of reading RawPath.
	RawPath string

	// RawFragment is an optional field containing an encoded fragment hint.
	// See the EscapedFragment method for more details.
	//
	// In general, code should call EscapedFragment instead of reading RawFragment.
	RawFragment string

	// ForceQuery indicates whether the original URL contained a query ('?') character.
	// When set, the String method will include a trailing '?', even when RawQuery is empty.
	ForceQuery bool

	// OmitHost indicates the URL has an empty host (authority).
	// When set, the String method will not include the host when it is empty.
	OmitHost bool
}

// User returns a [Userinfo] containing the provided username
// and no password set.
func ( string) *Userinfo {
	return &Userinfo{, "", false}
}

// UserPassword returns a [Userinfo] containing the provided username
// and password.
//
// This functionality should only be used with legacy web sites.
// RFC 2396 warns that interpreting Userinfo this way
// “is NOT RECOMMENDED, because the passing of authentication
// information in clear text (such as URI) has proven to be a
// security risk in almost every case where it has been used.”
func (,  string) *Userinfo {
	return &Userinfo{, , true}
}

// The Userinfo type is an immutable encapsulation of username and
// password details for a [URL]. An existing Userinfo value is guaranteed
// to have a username set (potentially empty, as allowed by RFC 2396),
// and optionally a password.
type Userinfo struct {
	username    string
	password    string
	passwordSet bool
}

// Username returns the username.
func ( *Userinfo) () string {
	if  == nil {
		return ""
	}
	return .username
}

// Password returns the password in case it is set, and whether it is set.
func ( *Userinfo) () (string, bool) {
	if  == nil {
		return "", false
	}
	return .password, .passwordSet
}

// String returns the encoded userinfo information in the standard form
// of "username[:password]".
func ( *Userinfo) () string {
	if  == nil {
		return ""
	}
	 := escape(.username, encodeUserPassword)
	if .passwordSet {
		 += ":" + escape(.password, encodeUserPassword)
	}
	return 
}

// Maybe rawURL is of the form scheme:path.
// (Scheme must be [a-zA-Z][a-zA-Z0-9+.-]*)
// If so, return scheme, path; else return "", rawURL.
func getScheme( string) (,  string,  error) {
	for  := 0;  < len(); ++ {
		 := []
		switch {
		case 'a' <=  &&  <= 'z' || 'A' <=  &&  <= 'Z':
		// do nothing
		case '0' <=  &&  <= '9' ||  == '+' ||  == '-' ||  == '.':
			if  == 0 {
				return "", , nil
			}
		case  == ':':
			if  == 0 {
				return "", "", errors.New("missing protocol scheme")
			}
			return [:], [+1:], nil
		default:
			// we have encountered an invalid character,
			// so there is no valid scheme
			return "", , nil
		}
	}
	return "", , nil
}

// Parse parses a raw url into a [URL] structure.
//
// The url may be relative (a path, without a host) or absolute
// (starting with a scheme). Trying to parse a hostname and path
// without a scheme is invalid but may not necessarily return an
// error, due to parsing ambiguities.
func ( string) (*URL, error) {
	// Cut off #frag
	, ,  := strings.Cut(, "#")
	,  := parse(, false)
	if  != nil {
		return nil, &Error{"parse", , }
	}
	if  == "" {
		return , nil
	}
	if  = .setFragment();  != nil {
		return nil, &Error{"parse", , }
	}
	return , nil
}

// ParseRequestURI parses a raw url into a [URL] structure. It assumes that
// url was received in an HTTP request, so the url is interpreted
// only as an absolute URI or an absolute path.
// The string url is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
func ( string) (*URL, error) {
	,  := parse(, true)
	if  != nil {
		return nil, &Error{"parse", , }
	}
	return , nil
}

// parse parses a URL from a string in one of two contexts. If
// viaRequest is true, the URL is assumed to have arrived via an HTTP request,
// in which case only absolute URLs or path-absolute relative URLs are allowed.
// If viaRequest is false, all forms of relative URLs are allowed.
func parse( string,  bool) (*URL, error) {
	var  string
	var  error

	if stringContainsCTLByte() {
		return nil, errors.New("net/url: invalid control character in URL")
	}

	if  == "" &&  {
		return nil, errors.New("empty url")
	}
	 := new(URL)

	if  == "*" {
		.Path = "*"
		return , nil
	}

	// Split off possible leading "http:", "mailto:", etc.
	// Cannot contain escaped characters.
	if .Scheme, ,  = getScheme();  != nil {
		return nil, 
	}
	.Scheme = strings.ToLower(.Scheme)

	if strings.HasSuffix(, "?") && strings.Count(, "?") == 1 {
		.ForceQuery = true
		 = [:len()-1]
	} else {
		, .RawQuery, _ = strings.Cut(, "?")
	}

	if !strings.HasPrefix(, "/") {
		if .Scheme != "" {
			// We consider rootless paths per RFC 3986 as opaque.
			.Opaque = 
			return , nil
		}
		if  {
			return nil, errors.New("invalid URI for request")
		}

		// Avoid confusion with malformed schemes, like cache_object:foo/bar.
		// See golang.org/issue/16822.
		//
		// RFC 3986, §3.3:
		// In addition, a URI reference (Section 4.1) may be a relative-path reference,
		// in which case the first path segment cannot contain a colon (":") character.
		if , ,  := strings.Cut(, "/"); strings.Contains(, ":") {
			// First path segment has colon. Not allowed in relative URL.
			return nil, errors.New("first path segment in URL cannot contain colon")
		}
	}

	if (.Scheme != "" || ! && !strings.HasPrefix(, "///")) && strings.HasPrefix(, "//") {
		var  string
		,  = [2:], ""
		if  := strings.Index(, "/");  >= 0 {
			,  = [:], [:]
		}
		.User, .Host,  = parseAuthority(.Scheme, )
		if  != nil {
			return nil, 
		}
	} else if .Scheme != "" && strings.HasPrefix(, "/") {
		// OmitHost is set to true when rawURL has an empty host (authority).
		// See golang.org/issue/46059.
		.OmitHost = true
	}

	// Set Path and, optionally, RawPath.
	// RawPath is a hint of the encoding of Path. We don't want to set it if
	// the default escaping of Path is equivalent, to help make sure that people
	// don't rely on it in general.
	if  := .setPath();  != nil {
		return nil, 
	}
	return , nil
}

func parseAuthority(,  string) ( *Userinfo,  string,  error) {
	 := strings.LastIndex(, "@")
	if  < 0 {
		,  = parseHost(, )
	} else {
		,  = parseHost(, [+1:])
	}
	if  != nil {
		return nil, "", 
	}
	if  < 0 {
		return nil, , nil
	}
	 := [:]
	if !validUserinfo() {
		return nil, "", errors.New("net/url: invalid userinfo")
	}
	if !strings.Contains(, ":") {
		if ,  = unescape(, encodeUserPassword);  != nil {
			return nil, "", 
		}
		 = User()
	} else {
		, ,  := strings.Cut(, ":")
		if ,  = unescape(, encodeUserPassword);  != nil {
			return nil, "", 
		}
		if ,  = unescape(, encodeUserPassword);  != nil {
			return nil, "", 
		}
		 = UserPassword(, )
	}
	return , , nil
}

// parseHost parses host as an authority without user
// information. That is, as host[:port].
func parseHost(,  string) (string, error) {
	if  := strings.LastIndex(, "[");  != -1 {
		// Parse an IP-Literal in RFC 3986 and RFC 6874.
		// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
		 := strings.LastIndex(, "]")
		if  < 0 {
			return "", errors.New("missing ']' in host")
		}

		 := [+1:]
		if !validOptionalPort() {
			return "", fmt.Errorf("invalid port %q after host", )
		}
		,  := unescape(, encodeHost)
		if  != nil {
			return "", 
		}

		 := [+1 : ]
		var  string
		// RFC 6874 defines that %25 (%-encoded percent) introduces
		// the zone identifier, and the zone identifier can use basically
		// any %-encoding it likes. That's different from the host, which
		// can only %-encode non-ASCII bytes.
		// We do impose some restrictions on the zone, to avoid stupidity
		// like newlines.
		 := strings.Index(, "%25")
		if  >= 0 {
			,  := unescape([:], encodeHost)
			if  != nil {
				return "", 
			}
			,  := unescape([:], encodeZone)
			if  != nil {
				return "", 
			}
			 =  + 
		} else {
			var  error
			,  = unescape(, encodeHost)
			if  != nil {
				return "", 
			}
		}

		// Per RFC 3986, only a host identified by a valid
		// IPv6 address can be enclosed by square brackets.
		// This excludes any IPv4, but notably not IPv4-mapped addresses.
		,  := netip.ParseAddr()
		if  != nil {
			return "", fmt.Errorf("invalid host: %w", )
		}
		if .Is4() {
			return "", errors.New("invalid IP-literal")
		}
		return "[" +  + "]" + , nil
	} else if  := strings.Index(, ":");  != -1 {
		 := strings.LastIndex(, ":")
		if  !=  {
			if  == "postgresql" ||  == "postgres" {
				// PostgreSQL relies on non-RFC-3986 parsing to accept
				// a comma-separated list of hosts (with optional ports)
				// in the host subcomponent:
				// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS
				//
				// Since we historically permitted colons to appear in the host,
				// continue to permit it for postgres:// URLs only.
				// https://go.dev/issue/75223
				 = 
			} else if urlstrictcolons.Value() == "0" {
				urlstrictcolons.IncNonDefault()
				 = 
			}
		}
		 := [:]
		if !validOptionalPort() {
			return "", fmt.Errorf("invalid port %q after host", )
		}
	}

	var  error
	if ,  = unescape(, encodeHost);  != nil {
		return "", 
	}
	return , nil
}

// setPath sets the Path and RawPath fields of the URL based on the provided
// escaped path p. It maintains the invariant that RawPath is only specified
// when it differs from the default encoding of the path.
// For example:
// - setPath("/foo/bar")   will set Path="/foo/bar" and RawPath=""
// - setPath("/foo%2fbar") will set Path="/foo/bar" and RawPath="/foo%2fbar"
// setPath will return an error only if the provided path contains an invalid
// escaping.
//
// setPath should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
//   - github.com/sagernet/sing
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:linkname badSetPath net/url.(*URL).setPath
func ( *URL) ( string) error {
	,  := unescape(, encodePath)
	if  != nil {
		return 
	}
	.Path = 
	if  := escape(, encodePath);  ==  {
		// Default encoding is fine.
		.RawPath = ""
	} else {
		.RawPath = 
	}
	return nil
}

// for linkname because we cannot linkname methods directly
func badSetPath(*URL, string) error

// EscapedPath returns the escaped form of u.Path.
// In general there are multiple possible escaped forms of any path.
// EscapedPath returns u.RawPath when it is a valid escaping of u.Path.
// Otherwise EscapedPath ignores u.RawPath and computes an escaped
// form on its own.
// The [URL.String] and [URL.RequestURI] methods use EscapedPath to construct
// their results.
// In general, code should call EscapedPath instead of
// reading u.RawPath directly.
func ( *URL) () string {
	if .RawPath != "" && validEncoded(.RawPath, encodePath) {
		,  := unescape(.RawPath, encodePath)
		if  == nil &&  == .Path {
			return .RawPath
		}
	}
	if .Path == "*" {
		return "*" // don't escape (Issue 11202)
	}
	return escape(.Path, encodePath)
}

// validEncoded reports whether s is a valid encoded path or fragment,
// according to mode.
// It must not contain any bytes that require escaping during encoding.
func validEncoded( string,  encoding) bool {
	for  := 0;  < len(); ++ {
		// RFC 3986, Appendix A.
		// pchar = unreserved / pct-encoded / sub-delims / ":" / "@".
		// shouldEscape is not quite compliant with the RFC,
		// so we check the sub-delims ourselves and let
		// shouldEscape handle the others.
		switch [] {
		case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '@':
			// ok
		case '[', ']':
			// ok - not specified in RFC 3986 but left alone by modern browsers
		case '%':
			// ok - percent encoded, will decode
		default:
			if shouldEscape([], ) {
				return false
			}
		}
	}
	return true
}

// setFragment is like setPath but for Fragment/RawFragment.
func ( *URL) ( string) error {
	,  := unescape(, encodeFragment)
	if  != nil {
		return 
	}
	.Fragment = 
	if  := escape(, encodeFragment);  ==  {
		// Default encoding is fine.
		.RawFragment = ""
	} else {
		.RawFragment = 
	}
	return nil
}

// EscapedFragment returns the escaped form of u.Fragment.
// In general there are multiple possible escaped forms of any fragment.
// EscapedFragment returns u.RawFragment when it is a valid escaping of u.Fragment.
// Otherwise EscapedFragment ignores u.RawFragment and computes an escaped
// form on its own.
// The [URL.String] method uses EscapedFragment to construct its result.
// In general, code should call EscapedFragment instead of
// reading u.RawFragment directly.
func ( *URL) () string {
	if .RawFragment != "" && validEncoded(.RawFragment, encodeFragment) {
		,  := unescape(.RawFragment, encodeFragment)
		if  == nil &&  == .Fragment {
			return .RawFragment
		}
	}
	return escape(.Fragment, encodeFragment)
}

// validOptionalPort reports whether port is either an empty string
// or matches /^:\d*$/
func validOptionalPort( string) bool {
	if  == "" {
		return true
	}
	if [0] != ':' {
		return false
	}
	for ,  := range [1:] {
		if  < '0' ||  > '9' {
			return false
		}
	}
	return true
}

// String reassembles the [URL] into a valid URL string.
// The general form of the result is one of:
//
//	scheme:opaque?query#fragment
//	scheme://userinfo@host/path?query#fragment
//
// If u.Opaque is non-empty, String uses the first form;
// otherwise it uses the second form.
// Any non-ASCII characters in host are escaped.
// To obtain the path, String uses u.EscapedPath().
//
// In the second form, the following rules apply:
//   - if u.Scheme is empty, scheme: is omitted.
//   - if u.User is nil, userinfo@ is omitted.
//   - if u.Host is empty, host/ is omitted.
//   - if u.Scheme and u.Host are empty and u.User is nil,
//     the entire scheme://userinfo@host/ is omitted.
//   - if u.Host is non-empty and u.Path begins with a /,
//     the form host/path does not add its own /.
//   - if u.RawQuery is empty, ?query is omitted.
//   - if u.Fragment is empty, #fragment is omitted.
func ( *URL) () string {
	var  strings.Builder

	 := len(.Scheme)
	if .Opaque != "" {
		 += len(.Opaque)
	} else {
		if !.OmitHost && (.Scheme != "" || .Host != "" || .User != nil) {
			 := .User.Username()
			,  := .User.Password()
			 += len() + len() + len(.Host)
		}
		 += len(.Path)
	}
	 += len(.RawQuery) + len(.RawFragment)
	 += len(":" + "//" + "//" + ":" + "@" + "/" + "./" + "?" + "#")
	.Grow()

	if .Scheme != "" {
		.WriteString(.Scheme)
		.WriteByte(':')
	}
	if .Opaque != "" {
		.WriteString(.Opaque)
	} else {
		if .Scheme != "" || .Host != "" || .User != nil {
			if .OmitHost && .Host == "" && .User == nil {
				// omit empty host
			} else {
				if .Host != "" || .Path != "" || .User != nil {
					.WriteString("//")
				}
				if  := .User;  != nil {
					.WriteString(.String())
					.WriteByte('@')
				}
				if  := .Host;  != "" {
					.WriteString(escape(, encodeHost))
				}
			}
		}
		 := .EscapedPath()
		if  != "" && [0] != '/' && .Host != "" {
			.WriteByte('/')
		}
		if .Len() == 0 {
			// RFC 3986 §4.2
			// A path segment that contains a colon character (e.g., "this:that")
			// cannot be used as the first segment of a relative-path reference, as
			// it would be mistaken for a scheme name. Such a segment must be
			// preceded by a dot-segment (e.g., "./this:that") to make a relative-
			// path reference.
			if , ,  := strings.Cut(, "/"); strings.Contains(, ":") {
				.WriteString("./")
			}
		}
		.WriteString()
	}
	if .ForceQuery || .RawQuery != "" {
		.WriteByte('?')
		.WriteString(.RawQuery)
	}
	if .Fragment != "" {
		.WriteByte('#')
		.WriteString(.EscapedFragment())
	}
	return .String()
}

// Redacted is like [URL.String] but replaces any password with "xxxxx".
// Only the password in u.User is redacted.
func ( *URL) () string {
	if  == nil {
		return ""
	}

	 := *
	if ,  := .User.Password();  {
		.User = UserPassword(.User.Username(), "xxxxx")
	}
	return .String()
}

// Values maps a string key to a list of values.
// It is typically used for query parameters and form values.
// Unlike in the http.Header map, the keys in a Values map
// are case-sensitive.
type Values map[string][]string

// Get gets the first value associated with the given key.
// If there are no values associated with the key, Get returns
// the empty string. To access multiple values, use the map
// directly.
func ( Values) ( string) string {
	 := []
	if len() == 0 {
		return ""
	}
	return [0]
}

// Set sets the key to value. It replaces any existing
// values.
func ( Values) (,  string) {
	[] = []string{}
}

// Add adds the value to key. It appends to any existing
// values associated with key.
func ( Values) (,  string) {
	[] = append([], )
}

// Del deletes the values associated with key.
func ( Values) ( string) {
	delete(, )
}

// Has checks whether a given key is set.
func ( Values) ( string) bool {
	,  := []
	return 
}

// ParseQuery parses the URL-encoded query string and returns
// a map listing the values specified for each key.
// ParseQuery always returns a non-nil map containing all the
// valid query parameters found; err describes the first decoding error
// encountered, if any.
//
// Query is expected to be a list of key=value settings separated by ampersands.
// A setting without an equals sign is interpreted as a key set to an empty
// value.
// Settings containing a non-URL-encoded semicolon are considered invalid.
func ( string) (Values, error) {
	 := make(Values)
	 := parseQuery(, )
	return , 
}

func parseQuery( Values,  string) ( error) {
	for  != "" {
		var  string
		, , _ = strings.Cut(, "&")
		if strings.Contains(, ";") {
			 = fmt.Errorf("invalid semicolon separator in query")
			continue
		}
		if  == "" {
			continue
		}
		, ,  := strings.Cut(, "=")
		,  := QueryUnescape()
		if  != nil {
			if  == nil {
				 = 
			}
			continue
		}
		,  = QueryUnescape()
		if  != nil {
			if  == nil {
				 = 
			}
			continue
		}
		[] = append([], )
	}
	return 
}

// Encode encodes the values into “URL encoded” form
// ("bar=baz&foo=quux") sorted by key.
func ( Values) () string {
	if len() == 0 {
		return ""
	}
	var  strings.Builder
	// To minimize allocations, we eschew iterators and pre-size the slice in
	// which we collect v's keys.
	 := make([]string, len())
	var  int
	for  := range  {
		[] = 
		++
	}
	slices.Sort()
	for ,  := range  {
		 := []
		 := QueryEscape()
		for ,  := range  {
			if .Len() > 0 {
				.WriteByte('&')
			}
			.WriteString()
			.WriteByte('=')
			.WriteString(QueryEscape())
		}
	}
	return .String()
}

// resolvePath applies special path segments from refs and applies
// them to base, per RFC 3986.
func resolvePath(,  string) string {
	var  string
	if  == "" {
		 = 
	} else if [0] != '/' {
		 := strings.LastIndex(, "/")
		 = [:+1] + 
	} else {
		 = 
	}
	if  == "" {
		return ""
	}

	var (
		 string
		  strings.Builder
	)
	 := true
	 := 
	// We want to return a leading '/', so write it now.
	.WriteByte('/')
	 := true
	for  {
		, ,  = strings.Cut(, "/")
		if  == "." {
			 = false
			// drop
			continue
		}

		if  == ".." {
			// Ignore the leading '/' we already wrote.
			 := .String()[1:]
			 := strings.LastIndexByte(, '/')

			.Reset()
			.WriteByte('/')
			if  == -1 {
				 = true
			} else {
				.WriteString([:])
			}
		} else {
			if ! {
				.WriteByte('/')
			}
			.WriteString()
			 = false
		}
	}

	if  == "." ||  == ".." {
		.WriteByte('/')
	}

	// We wrote an initial '/', but we don't want two.
	 := .String()
	if len() > 1 && [1] == '/' {
		 = [1:]
	}
	return 
}

// IsAbs reports whether the [URL] is absolute.
// Absolute means that it has a non-empty scheme.
func ( *URL) () bool {
	return .Scheme != ""
}

// Parse parses a [URL] in the context of the receiver. The provided URL
// may be relative or absolute. Parse returns nil, err on parse
// failure, otherwise its return value is the same as [URL.ResolveReference].
func ( *URL) ( string) (*URL, error) {
	,  := Parse()
	if  != nil {
		return nil, 
	}
	return .ResolveReference(), nil
}

// ResolveReference resolves a URI reference to an absolute URI from
// an absolute base URI u, per RFC 3986 Section 5.2. The URI reference
// may be relative or absolute. ResolveReference always returns a new
// [URL] instance, even if the returned URL is identical to either the
// base or reference. If ref is an absolute URL, then ResolveReference
// ignores base and returns a copy of ref.
func ( *URL) ( *URL) *URL {
	 := *
	if .Scheme == "" {
		.Scheme = .Scheme
	}
	if .Scheme != "" || .Host != "" || .User != nil {
		// The "absoluteURI" or "net_path" cases.
		// We can ignore the error from setPath since we know we provided a
		// validly-escaped path.
		.setPath(resolvePath(.EscapedPath(), ""))
		return &
	}
	if .Opaque != "" {
		.User = nil
		.Host = ""
		.Path = ""
		return &
	}
	if .Path == "" && !.ForceQuery && .RawQuery == "" {
		.RawQuery = .RawQuery
		if .Fragment == "" {
			.Fragment = .Fragment
			.RawFragment = .RawFragment
		}
	}
	if .Path == "" && .Opaque != "" {
		.Opaque = .Opaque
		.User = nil
		.Host = ""
		.Path = ""
		return &
	}
	// The "abs_path" or "rel_path" cases.
	.Host = .Host
	.User = .User
	.setPath(resolvePath(.EscapedPath(), .EscapedPath()))
	return &
}

// Query parses RawQuery and returns the corresponding values.
// It silently discards malformed value pairs.
// To check errors use [ParseQuery].
func ( *URL) () Values {
	,  := ParseQuery(.RawQuery)
	return 
}

// RequestURI returns the encoded path?query or opaque?query
// string that would be used in an HTTP request for u.
func ( *URL) () string {
	 := .Opaque
	if  == "" {
		 = .EscapedPath()
		if  == "" {
			 = "/"
		}
	} else {
		if strings.HasPrefix(, "//") {
			 = .Scheme + ":" + 
		}
	}
	if .ForceQuery || .RawQuery != "" {
		 += "?" + .RawQuery
	}
	return 
}

// Hostname returns u.Host, stripping any valid port number if present.
//
// If the result is enclosed in square brackets, as literal IPv6 addresses are,
// the square brackets are removed from the result.
func ( *URL) () string {
	,  := splitHostPort(.Host)
	return 
}

// Port returns the port part of u.Host, without the leading colon.
//
// If u.Host doesn't contain a valid numeric port, Port returns an empty string.
func ( *URL) () string {
	,  := splitHostPort(.Host)
	return 
}

// splitHostPort separates host and port. If the port is not valid, it returns
// the entire input as host, and it doesn't check the validity of the host.
// Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.
func splitHostPort( string) (,  string) {
	 = 

	 := strings.LastIndexByte(, ':')
	if  != -1 && validOptionalPort([:]) {
		,  = [:], [+1:]
	}

	if strings.HasPrefix(, "[") && strings.HasSuffix(, "]") {
		 = [1 : len()-1]
	}

	return
}

// Marshaling interface implementations.
// Would like to implement MarshalText/UnmarshalText but that will change the JSON representation of URLs.

func ( *URL) () ( []byte,  error) {
	return .AppendBinary(nil)
}

func ( *URL) ( []byte) ([]byte, error) {
	return append(, .String()...), nil
}

func ( *URL) ( []byte) error {
	,  := Parse(string())
	if  != nil {
		return 
	}
	* = *
	return nil
}

// JoinPath returns a new [URL] with the provided path elements joined to
// any existing path and the resulting path cleaned of any ./ or ../ elements.
// Any sequences of multiple / characters will be reduced to a single /.
// Path elements must already be in escaped form, as produced by [PathEscape].
func ( *URL) ( ...string) *URL {
	,  := .joinPath(...)
	return 
}

func ( *URL) ( ...string) (*URL, error) {
	 = append([]string{.EscapedPath()}, ...)
	var  string
	if !strings.HasPrefix([0], "/") {
		// Return a relative path if u is relative,
		// but ensure that it contains no ../ elements.
		[0] = "/" + [0]
		 = path.Join(...)[1:]
	} else {
		 = path.Join(...)
	}
	// path.Join will remove any trailing slashes.
	// Preserve at least one.
	if strings.HasSuffix([len()-1], "/") && !strings.HasSuffix(, "/") {
		 += "/"
	}
	 := *
	 := .setPath()
	return &, 
}

// validUserinfo reports whether s is a valid userinfo string per RFC 3986
// Section 3.2.1:
//
//	userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
//	unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
//	sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
//	              / "*" / "+" / "," / ";" / "="
//
// It doesn't validate pct-encoded. The caller does that via func unescape.
func validUserinfo( string) bool {
	for ,  := range  {
		if 'A' <=  &&  <= 'Z' {
			continue
		}
		if 'a' <=  &&  <= 'z' {
			continue
		}
		if '0' <=  &&  <= '9' {
			continue
		}
		switch  {
		case '-', '.', '_', ':', '~', '!', '$', '&', '\'',
			'(', ')', '*', '+', ',', ';', '=', '%':
			continue
		case '@':
			// `RFC 3986 section 3.2.1` does not allow '@' in userinfo.
			// It is a delimiter between userinfo and host.
			// However, URLs are diverse, and in some cases,
			// the userinfo may contain an '@' character,
			// for example, in "http://username:p@ssword@google.com",
			// the string "username:p@ssword" should be treated as valid userinfo.
			// Ref:
			//   https://go.dev/issue/3439
			//   https://go.dev/issue/22655
			continue
		default:
			return false
		}
	}
	return true
}

// stringContainsCTLByte reports whether s contains any ASCII control character.
func stringContainsCTLByte( string) bool {
	for  := 0;  < len(); ++ {
		 := []
		if  < ' ' ||  == 0x7f {
			return true
		}
	}
	return false
}

// JoinPath returns a [URL] string with the provided path elements joined to
// the existing path of base and the resulting path cleaned of any ./ or ../ elements.
// Path elements must already be in escaped form, as produced by [PathEscape].
func ( string,  ...string) ( string,  error) {
	,  := Parse()
	if  != nil {
		return
	}
	,  := .joinPath(...)
	if  != nil {
		return "", 
	}
	return .String(), nil
}