src/net/url/gen_encoding_table.go GO 235 lines View on github.com → Search inside
1// Copyright 2025 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45//go:build ignore67package main89import (10	"bytes"11	_ "embed"12	"fmt"13	"go/format"14	"io"15	"log"16	"maps"17	"os"18	"slices"19	"strconv"20	"strings"21)2223// We embed this source file in the resulting code-generation program in order24// to extract the definitions of the encoding type and constants from it and25// include them in the generated file.26//27//go:embed gen_encoding_table.go28var genSource string2930const filename = "encoding_table.go"3132func main() {33	var out bytes.Buffer34	fmt.Fprintln(&out, "// Code generated from gen_encoding_table.go using 'go generate'; DO NOT EDIT.")35	fmt.Fprintln(&out)36	fmt.Fprintln(&out, "// Copyright 2025 The Go Authors. All rights reserved.")37	fmt.Fprintln(&out, "// Use of this source code is governed by a BSD-style")38	fmt.Fprintln(&out, "// license that can be found in the LICENSE file.")39	fmt.Fprintln(&out)40	fmt.Fprintln(&out, "package url")41	fmt.Fprintln(&out)42	generateEnc(&out, genSource)43	generateTable(&out)4445	formatted, err := format.Source(out.Bytes())46	if err != nil {47		log.Fatal("format:", err)48	}4950	err = os.WriteFile(filename, formatted, 0644)51	if err != nil {52		log.Fatal("WriteFile:", err)53	}54}5556func generateEnc(w io.Writer, src string) {57	var writeLine bool58	for line := range strings.Lines(src) {59		if strings.HasPrefix(line, "// START encoding") {60			writeLine = true61			continue62		}63		if strings.HasPrefix(line, "// END encoding") {64			return65		}66		if writeLine {67			fmt.Fprint(w, line)68		}69	}70}7172func generateTable(w io.Writer) {73	fmt.Fprintln(w, "var table = [256]encoding{")7475	// Sort the encodings (in decreasing order) to guarantee a stable output.76	sortedEncs := slices.Sorted(maps.Keys(encNames))77	slices.Reverse(sortedEncs)7879	for i := range 256 {80		c := byte(i)81		var lineBuf bytes.Buffer8283		// Write key to line buffer.84		lineBuf.WriteString(strconv.QuoteRune(rune(c)))8586		lineBuf.WriteByte(':')8788		// Write value to line buffer.89		blankVal := true90		if ishex(c) {91			// Set the hexChar bit if this char is hexadecimal.92			lineBuf.WriteString("hexChar")93			blankVal = false94		}95		for _, enc := range sortedEncs {96			if !shouldEscape(c, enc) {97				if !blankVal {98					lineBuf.WriteByte('|')99				}100				// Set this encoding mode's bit if this char should NOT be101				// escaped.102				name := encNames[enc]103				lineBuf.WriteString(name)104				blankVal = false105			}106		}107108		if !blankVal {109			lineBuf.WriteString(",\n")110			w.Write(lineBuf.Bytes())111		}112	}113	fmt.Fprintln(w, "}")114}115116// START encoding (keep this marker comment in sync with genEnc)117type encoding uint8118119const (120	encodePath encoding = 1 << iota121	encodePathSegment122	encodeHost123	encodeZone124	encodeUserPassword125	encodeQueryComponent126	encodeFragment127128	// hexChar is actually NOT an encoding mode, but there are only seven129	// encoding modes. We might as well abuse the otherwise unused most130	// significant bit in uint8 to indicate whether a character is131	// hexadecimal.132	hexChar133)134135// END encoding (keep this marker comment in sync with genEnc)136137// Keep this in sync with the definitions of encoding mode constants.138var encNames = map[encoding]string{139	encodePath:           "encodePath",140	encodePathSegment:    "encodePathSegment",141	encodeHost:           "encodeHost",142	encodeZone:           "encodeZone",143	encodeUserPassword:   "encodeUserPassword",144	encodeQueryComponent: "encodeQueryComponent",145	encodeFragment:       "encodeFragment",146}147148// Return true if the specified character should be escaped when149// appearing in a URL string, according to RFC 3986.150//151// Please be informed that for now shouldEscape does not check all152// reserved characters correctly. See golang.org/issue/5684.153func shouldEscape(c byte, mode encoding) bool {154	// §2.3 Unreserved characters (alphanum)155	if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {156		return false157	}158159	if mode == encodeHost || mode == encodeZone {160		// §3.2.2 Host allows161		//	sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="162		// as part of reg-name.163		// We add : because we include :port as part of host.164		// We add [ ] because we include [ipv6]:port as part of host.165		// We add < > because they're the only characters left that166		// we could possibly allow, and Parse will reject them if we167		// escape them (because hosts can't use %-encoding for168		// ASCII bytes).169		switch c {170		case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':171			return false172		}173	}174175	switch c {176	case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)177		return false178179	case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)180		// Different sections of the URL allow a few of181		// the reserved characters to appear unescaped.182		switch mode {183		case encodePath: // §3.3184			// The RFC allows : @ & = + $ but saves / ; , for assigning185			// meaning to individual path segments. This package186			// only manipulates the path as a whole, so we allow those187			// last three as well. That leaves only ? to escape.188			return c == '?'189190		case encodePathSegment: // §3.3191			// The RFC allows : @ & = + $ but saves / ; , for assigning192			// meaning to individual path segments.193			return c == '/' || c == ';' || c == ',' || c == '?'194195		case encodeUserPassword: // §3.2.1196			// The RFC allows ';', ':', '&', '=', '+', '$', and ',' in197			// userinfo, so we must escape only '@', '/', and '?'.198			// The parsing of userinfo treats ':' as special so we must escape199			// that too.200			return c == '@' || c == '/' || c == '?' || c == ':'201202		case encodeQueryComponent: // §3.4203			// The RFC reserves (so we must escape) everything.204			return true205206		case encodeFragment: // §4.1207			// The RFC text is silent but the grammar allows208			// everything, so escape nothing.209			return false210		}211	}212213	if mode == encodeFragment {214		// RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are215		// included in reserved from RFC 2396 §2.2. The remaining sub-delims do not216		// need to be escaped. To minimize potential breakage, we apply two restrictions:217		// (1) we always escape sub-delims outside of the fragment, and (2) we always218		// escape single quote to avoid breaking callers that had previously assumed that219		// single quotes would be escaped. See issue #19917.220		switch c {221		case '!', '(', ')', '*':222			return false223		}224	}225226	// Everything else must be escaped.227	return true228}229230func ishex(c byte) bool {231	return '0' <= c && c <= '9' ||232		'a' <= c && c <= 'f' ||233		'A' <= c && c <= 'F'234}

Code quality findings 1

Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var encNames = map[encoding]string{

Security findings 1

Detected a potential hardcoded password literal, which may expose account credentials.
info security generic-password low confidence

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings