Source file
src/net/url/gen_encoding_table.go
1
2
3
4
5
6
7 package main
8
9 import (
10 "bytes"
11 _ "embed"
12 "fmt"
13 "go/format"
14 "io"
15 "log"
16 "maps"
17 "os"
18 "slices"
19 "strconv"
20 "strings"
21 )
22
23
24
25
26
27
28 var genSource string
29
30 const filename = "encoding_table.go"
31
32 func main() {
33 var out bytes.Buffer
34 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)
44
45 formatted, err := format.Source(out.Bytes())
46 if err != nil {
47 log.Fatal("format:", err)
48 }
49
50 err = os.WriteFile(filename, formatted, 0644)
51 if err != nil {
52 log.Fatal("WriteFile:", err)
53 }
54 }
55
56 func generateEnc(w io.Writer, src string) {
57 var writeLine bool
58 for line := range strings.Lines(src) {
59 if strings.HasPrefix(line, "// START encoding") {
60 writeLine = true
61 continue
62 }
63 if strings.HasPrefix(line, "// END encoding") {
64 return
65 }
66 if writeLine {
67 fmt.Fprint(w, line)
68 }
69 }
70 }
71
72 func generateTable(w io.Writer) {
73 fmt.Fprintln(w, "var table = [256]encoding{")
74
75
76 sortedEncs := slices.Sorted(maps.Keys(encNames))
77 slices.Reverse(sortedEncs)
78
79 for i := range 256 {
80 c := byte(i)
81 var lineBuf bytes.Buffer
82
83
84 lineBuf.WriteString(strconv.QuoteRune(rune(c)))
85
86 lineBuf.WriteByte(':')
87
88
89 blankVal := true
90 if ishex(c) {
91
92 lineBuf.WriteString("hexChar")
93 blankVal = false
94 }
95 for _, enc := range sortedEncs {
96 if !shouldEscape(c, enc) {
97 if !blankVal {
98 lineBuf.WriteByte('|')
99 }
100
101
102 name := encNames[enc]
103 lineBuf.WriteString(name)
104 blankVal = false
105 }
106 }
107
108 if !blankVal {
109 lineBuf.WriteString(",\n")
110 w.Write(lineBuf.Bytes())
111 }
112 }
113 fmt.Fprintln(w, "}")
114 }
115
116
117 type encoding uint8
118
119 const (
120 encodePath encoding = 1 << iota
121 encodePathSegment
122 encodeHost
123 encodeZone
124 encodeUserPassword
125 encodeQueryComponent
126 encodeFragment
127
128
129
130
131
132 hexChar
133 )
134
135
136
137
138 var 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 }
147
148
149
150
151
152
153 func shouldEscape(c byte, mode encoding) bool {
154
155 if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
156 return false
157 }
158
159 if mode == encodeHost || mode == encodeZone {
160
161
162
163
164
165
166
167
168
169 switch c {
170 case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
171 return false
172 }
173 }
174
175 switch c {
176 case '-', '_', '.', '~':
177 return false
178
179 case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@':
180
181
182 switch mode {
183 case encodePath:
184
185
186
187
188 return c == '?'
189
190 case encodePathSegment:
191
192
193 return c == '/' || c == ';' || c == ',' || c == '?'
194
195 case encodeUserPassword:
196
197
198
199
200 return c == '@' || c == '/' || c == '?' || c == ':'
201
202 case encodeQueryComponent:
203
204 return true
205
206 case encodeFragment:
207
208
209 return false
210 }
211 }
212
213 if mode == encodeFragment {
214
215
216
217
218
219
220 switch c {
221 case '!', '(', ')', '*':
222 return false
223 }
224 }
225
226
227 return true
228 }
229
230 func ishex(c byte) bool {
231 return '0' <= c && c <= '9' ||
232 'a' <= c && c <= 'f' ||
233 'A' <= c && c <= 'F'
234 }
235
View as plain text