1 | package fasthttp
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bytes"
|
---|
5 | "errors"
|
---|
6 | "io"
|
---|
7 | "sort"
|
---|
8 | "sync"
|
---|
9 |
|
---|
10 | "github.com/valyala/bytebufferpool"
|
---|
11 | )
|
---|
12 |
|
---|
13 | const (
|
---|
14 | argsNoValue = true
|
---|
15 | argsHasValue = false
|
---|
16 | )
|
---|
17 |
|
---|
18 | // AcquireArgs returns an empty Args object from the pool.
|
---|
19 | //
|
---|
20 | // The returned Args may be returned to the pool with ReleaseArgs
|
---|
21 | // when no longer needed. This allows reducing GC load.
|
---|
22 | func AcquireArgs() *Args {
|
---|
23 | return argsPool.Get().(*Args)
|
---|
24 | }
|
---|
25 |
|
---|
26 | // ReleaseArgs returns the object acquired via AcquireArgs to the pool.
|
---|
27 | //
|
---|
28 | // Do not access the released Args object, otherwise data races may occur.
|
---|
29 | func ReleaseArgs(a *Args) {
|
---|
30 | a.Reset()
|
---|
31 | argsPool.Put(a)
|
---|
32 | }
|
---|
33 |
|
---|
34 | var argsPool = &sync.Pool{
|
---|
35 | New: func() interface{} {
|
---|
36 | return &Args{}
|
---|
37 | },
|
---|
38 | }
|
---|
39 |
|
---|
40 | // Args represents query arguments.
|
---|
41 | //
|
---|
42 | // It is forbidden copying Args instances. Create new instances instead
|
---|
43 | // and use CopyTo().
|
---|
44 | //
|
---|
45 | // Args instance MUST NOT be used from concurrently running goroutines.
|
---|
46 | type Args struct {
|
---|
47 | noCopy noCopy //nolint:unused,structcheck
|
---|
48 |
|
---|
49 | args []argsKV
|
---|
50 | buf []byte
|
---|
51 | }
|
---|
52 |
|
---|
53 | type argsKV struct {
|
---|
54 | key []byte
|
---|
55 | value []byte
|
---|
56 | noValue bool
|
---|
57 | }
|
---|
58 |
|
---|
59 | // Reset clears query args.
|
---|
60 | func (a *Args) Reset() {
|
---|
61 | a.args = a.args[:0]
|
---|
62 | }
|
---|
63 |
|
---|
64 | // CopyTo copies all args to dst.
|
---|
65 | func (a *Args) CopyTo(dst *Args) {
|
---|
66 | dst.Reset()
|
---|
67 | dst.args = copyArgs(dst.args, a.args)
|
---|
68 | }
|
---|
69 |
|
---|
70 | // VisitAll calls f for each existing arg.
|
---|
71 | //
|
---|
72 | // f must not retain references to key and value after returning.
|
---|
73 | // Make key and/or value copies if you need storing them after returning.
|
---|
74 | func (a *Args) VisitAll(f func(key, value []byte)) {
|
---|
75 | visitArgs(a.args, f)
|
---|
76 | }
|
---|
77 |
|
---|
78 | // Len returns the number of query args.
|
---|
79 | func (a *Args) Len() int {
|
---|
80 | return len(a.args)
|
---|
81 | }
|
---|
82 |
|
---|
83 | // Parse parses the given string containing query args.
|
---|
84 | func (a *Args) Parse(s string) {
|
---|
85 | a.buf = append(a.buf[:0], s...)
|
---|
86 | a.ParseBytes(a.buf)
|
---|
87 | }
|
---|
88 |
|
---|
89 | // ParseBytes parses the given b containing query args.
|
---|
90 | func (a *Args) ParseBytes(b []byte) {
|
---|
91 | a.Reset()
|
---|
92 |
|
---|
93 | var s argsScanner
|
---|
94 | s.b = b
|
---|
95 |
|
---|
96 | var kv *argsKV
|
---|
97 | a.args, kv = allocArg(a.args)
|
---|
98 | for s.next(kv) {
|
---|
99 | if len(kv.key) > 0 || len(kv.value) > 0 {
|
---|
100 | a.args, kv = allocArg(a.args)
|
---|
101 | }
|
---|
102 | }
|
---|
103 | a.args = releaseArg(a.args)
|
---|
104 | }
|
---|
105 |
|
---|
106 | // String returns string representation of query args.
|
---|
107 | func (a *Args) String() string {
|
---|
108 | return string(a.QueryString())
|
---|
109 | }
|
---|
110 |
|
---|
111 | // QueryString returns query string for the args.
|
---|
112 | //
|
---|
113 | // The returned value is valid until the Args is reused or released (ReleaseArgs).
|
---|
114 | // Do not store references to the returned value. Make copies instead.
|
---|
115 | func (a *Args) QueryString() []byte {
|
---|
116 | a.buf = a.AppendBytes(a.buf[:0])
|
---|
117 | return a.buf
|
---|
118 | }
|
---|
119 |
|
---|
120 | // Sort sorts Args by key and then value using 'f' as comparison function.
|
---|
121 | //
|
---|
122 | // For example args.Sort(bytes.Compare)
|
---|
123 | func (a *Args) Sort(f func(x, y []byte) int) {
|
---|
124 | sort.SliceStable(a.args, func(i, j int) bool {
|
---|
125 | n := f(a.args[i].key, a.args[j].key)
|
---|
126 | if n == 0 {
|
---|
127 | return f(a.args[i].value, a.args[j].value) == -1
|
---|
128 | }
|
---|
129 | return n == -1
|
---|
130 | })
|
---|
131 | }
|
---|
132 |
|
---|
133 | // AppendBytes appends query string to dst and returns the extended dst.
|
---|
134 | func (a *Args) AppendBytes(dst []byte) []byte {
|
---|
135 | for i, n := 0, len(a.args); i < n; i++ {
|
---|
136 | kv := &a.args[i]
|
---|
137 | dst = AppendQuotedArg(dst, kv.key)
|
---|
138 | if !kv.noValue {
|
---|
139 | dst = append(dst, '=')
|
---|
140 | if len(kv.value) > 0 {
|
---|
141 | dst = AppendQuotedArg(dst, kv.value)
|
---|
142 | }
|
---|
143 | }
|
---|
144 | if i+1 < n {
|
---|
145 | dst = append(dst, '&')
|
---|
146 | }
|
---|
147 | }
|
---|
148 | return dst
|
---|
149 | }
|
---|
150 |
|
---|
151 | // WriteTo writes query string to w.
|
---|
152 | //
|
---|
153 | // WriteTo implements io.WriterTo interface.
|
---|
154 | func (a *Args) WriteTo(w io.Writer) (int64, error) {
|
---|
155 | n, err := w.Write(a.QueryString())
|
---|
156 | return int64(n), err
|
---|
157 | }
|
---|
158 |
|
---|
159 | // Del deletes argument with the given key from query args.
|
---|
160 | func (a *Args) Del(key string) {
|
---|
161 | a.args = delAllArgs(a.args, key)
|
---|
162 | }
|
---|
163 |
|
---|
164 | // DelBytes deletes argument with the given key from query args.
|
---|
165 | func (a *Args) DelBytes(key []byte) {
|
---|
166 | a.args = delAllArgs(a.args, b2s(key))
|
---|
167 | }
|
---|
168 |
|
---|
169 | // Add adds 'key=value' argument.
|
---|
170 | //
|
---|
171 | // Multiple values for the same key may be added.
|
---|
172 | func (a *Args) Add(key, value string) {
|
---|
173 | a.args = appendArg(a.args, key, value, argsHasValue)
|
---|
174 | }
|
---|
175 |
|
---|
176 | // AddBytesK adds 'key=value' argument.
|
---|
177 | //
|
---|
178 | // Multiple values for the same key may be added.
|
---|
179 | func (a *Args) AddBytesK(key []byte, value string) {
|
---|
180 | a.args = appendArg(a.args, b2s(key), value, argsHasValue)
|
---|
181 | }
|
---|
182 |
|
---|
183 | // AddBytesV adds 'key=value' argument.
|
---|
184 | //
|
---|
185 | // Multiple values for the same key may be added.
|
---|
186 | func (a *Args) AddBytesV(key string, value []byte) {
|
---|
187 | a.args = appendArg(a.args, key, b2s(value), argsHasValue)
|
---|
188 | }
|
---|
189 |
|
---|
190 | // AddBytesKV adds 'key=value' argument.
|
---|
191 | //
|
---|
192 | // Multiple values for the same key may be added.
|
---|
193 | func (a *Args) AddBytesKV(key, value []byte) {
|
---|
194 | a.args = appendArg(a.args, b2s(key), b2s(value), argsHasValue)
|
---|
195 | }
|
---|
196 |
|
---|
197 | // AddNoValue adds only 'key' as argument without the '='.
|
---|
198 | //
|
---|
199 | // Multiple values for the same key may be added.
|
---|
200 | func (a *Args) AddNoValue(key string) {
|
---|
201 | a.args = appendArg(a.args, key, "", argsNoValue)
|
---|
202 | }
|
---|
203 |
|
---|
204 | // AddBytesKNoValue adds only 'key' as argument without the '='.
|
---|
205 | //
|
---|
206 | // Multiple values for the same key may be added.
|
---|
207 | func (a *Args) AddBytesKNoValue(key []byte) {
|
---|
208 | a.args = appendArg(a.args, b2s(key), "", argsNoValue)
|
---|
209 | }
|
---|
210 |
|
---|
211 | // Set sets 'key=value' argument.
|
---|
212 | func (a *Args) Set(key, value string) {
|
---|
213 | a.args = setArg(a.args, key, value, argsHasValue)
|
---|
214 | }
|
---|
215 |
|
---|
216 | // SetBytesK sets 'key=value' argument.
|
---|
217 | func (a *Args) SetBytesK(key []byte, value string) {
|
---|
218 | a.args = setArg(a.args, b2s(key), value, argsHasValue)
|
---|
219 | }
|
---|
220 |
|
---|
221 | // SetBytesV sets 'key=value' argument.
|
---|
222 | func (a *Args) SetBytesV(key string, value []byte) {
|
---|
223 | a.args = setArg(a.args, key, b2s(value), argsHasValue)
|
---|
224 | }
|
---|
225 |
|
---|
226 | // SetBytesKV sets 'key=value' argument.
|
---|
227 | func (a *Args) SetBytesKV(key, value []byte) {
|
---|
228 | a.args = setArgBytes(a.args, key, value, argsHasValue)
|
---|
229 | }
|
---|
230 |
|
---|
231 | // SetNoValue sets only 'key' as argument without the '='.
|
---|
232 | //
|
---|
233 | // Only key in argumemt, like key1&key2
|
---|
234 | func (a *Args) SetNoValue(key string) {
|
---|
235 | a.args = setArg(a.args, key, "", argsNoValue)
|
---|
236 | }
|
---|
237 |
|
---|
238 | // SetBytesKNoValue sets 'key' argument.
|
---|
239 | func (a *Args) SetBytesKNoValue(key []byte) {
|
---|
240 | a.args = setArg(a.args, b2s(key), "", argsNoValue)
|
---|
241 | }
|
---|
242 |
|
---|
243 | // Peek returns query arg value for the given key.
|
---|
244 | //
|
---|
245 | // The returned value is valid until the Args is reused or released (ReleaseArgs).
|
---|
246 | // Do not store references to the returned value. Make copies instead.
|
---|
247 | func (a *Args) Peek(key string) []byte {
|
---|
248 | return peekArgStr(a.args, key)
|
---|
249 | }
|
---|
250 |
|
---|
251 | // PeekBytes returns query arg value for the given key.
|
---|
252 | //
|
---|
253 | // The returned value is valid until the Args is reused or released (ReleaseArgs).
|
---|
254 | // Do not store references to the returned value. Make copies instead.
|
---|
255 | func (a *Args) PeekBytes(key []byte) []byte {
|
---|
256 | return peekArgBytes(a.args, key)
|
---|
257 | }
|
---|
258 |
|
---|
259 | // PeekMulti returns all the arg values for the given key.
|
---|
260 | func (a *Args) PeekMulti(key string) [][]byte {
|
---|
261 | var values [][]byte
|
---|
262 | a.VisitAll(func(k, v []byte) {
|
---|
263 | if string(k) == key {
|
---|
264 | values = append(values, v)
|
---|
265 | }
|
---|
266 | })
|
---|
267 | return values
|
---|
268 | }
|
---|
269 |
|
---|
270 | // PeekMultiBytes returns all the arg values for the given key.
|
---|
271 | func (a *Args) PeekMultiBytes(key []byte) [][]byte {
|
---|
272 | return a.PeekMulti(b2s(key))
|
---|
273 | }
|
---|
274 |
|
---|
275 | // Has returns true if the given key exists in Args.
|
---|
276 | func (a *Args) Has(key string) bool {
|
---|
277 | return hasArg(a.args, key)
|
---|
278 | }
|
---|
279 |
|
---|
280 | // HasBytes returns true if the given key exists in Args.
|
---|
281 | func (a *Args) HasBytes(key []byte) bool {
|
---|
282 | return hasArg(a.args, b2s(key))
|
---|
283 | }
|
---|
284 |
|
---|
285 | // ErrNoArgValue is returned when Args value with the given key is missing.
|
---|
286 | var ErrNoArgValue = errors.New("no Args value for the given key")
|
---|
287 |
|
---|
288 | // GetUint returns uint value for the given key.
|
---|
289 | func (a *Args) GetUint(key string) (int, error) {
|
---|
290 | value := a.Peek(key)
|
---|
291 | if len(value) == 0 {
|
---|
292 | return -1, ErrNoArgValue
|
---|
293 | }
|
---|
294 | return ParseUint(value)
|
---|
295 | }
|
---|
296 |
|
---|
297 | // SetUint sets uint value for the given key.
|
---|
298 | func (a *Args) SetUint(key string, value int) {
|
---|
299 | bb := bytebufferpool.Get()
|
---|
300 | bb.B = AppendUint(bb.B[:0], value)
|
---|
301 | a.SetBytesV(key, bb.B)
|
---|
302 | bytebufferpool.Put(bb)
|
---|
303 | }
|
---|
304 |
|
---|
305 | // SetUintBytes sets uint value for the given key.
|
---|
306 | func (a *Args) SetUintBytes(key []byte, value int) {
|
---|
307 | a.SetUint(b2s(key), value)
|
---|
308 | }
|
---|
309 |
|
---|
310 | // GetUintOrZero returns uint value for the given key.
|
---|
311 | //
|
---|
312 | // Zero (0) is returned on error.
|
---|
313 | func (a *Args) GetUintOrZero(key string) int {
|
---|
314 | n, err := a.GetUint(key)
|
---|
315 | if err != nil {
|
---|
316 | n = 0
|
---|
317 | }
|
---|
318 | return n
|
---|
319 | }
|
---|
320 |
|
---|
321 | // GetUfloat returns ufloat value for the given key.
|
---|
322 | func (a *Args) GetUfloat(key string) (float64, error) {
|
---|
323 | value := a.Peek(key)
|
---|
324 | if len(value) == 0 {
|
---|
325 | return -1, ErrNoArgValue
|
---|
326 | }
|
---|
327 | return ParseUfloat(value)
|
---|
328 | }
|
---|
329 |
|
---|
330 | // GetUfloatOrZero returns ufloat value for the given key.
|
---|
331 | //
|
---|
332 | // Zero (0) is returned on error.
|
---|
333 | func (a *Args) GetUfloatOrZero(key string) float64 {
|
---|
334 | f, err := a.GetUfloat(key)
|
---|
335 | if err != nil {
|
---|
336 | f = 0
|
---|
337 | }
|
---|
338 | return f
|
---|
339 | }
|
---|
340 |
|
---|
341 | // GetBool returns boolean value for the given key.
|
---|
342 | //
|
---|
343 | // true is returned for "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes",
|
---|
344 | // otherwise false is returned.
|
---|
345 | func (a *Args) GetBool(key string) bool {
|
---|
346 | switch b2s(a.Peek(key)) {
|
---|
347 | // Support the same true cases as strconv.ParseBool
|
---|
348 | // See: https://github.com/golang/go/blob/4e1b11e2c9bdb0ddea1141eed487be1a626ff5be/src/strconv/atob.go#L12
|
---|
349 | // and Y and Yes versions.
|
---|
350 | case "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes":
|
---|
351 | return true
|
---|
352 | default:
|
---|
353 | return false
|
---|
354 | }
|
---|
355 | }
|
---|
356 |
|
---|
357 | func visitArgs(args []argsKV, f func(k, v []byte)) {
|
---|
358 | for i, n := 0, len(args); i < n; i++ {
|
---|
359 | kv := &args[i]
|
---|
360 | f(kv.key, kv.value)
|
---|
361 | }
|
---|
362 | }
|
---|
363 |
|
---|
364 | func visitArgsKey(args []argsKV, f func(k []byte)) {
|
---|
365 | for i, n := 0, len(args); i < n; i++ {
|
---|
366 | kv := &args[i]
|
---|
367 | f(kv.key)
|
---|
368 | }
|
---|
369 | }
|
---|
370 |
|
---|
371 | func copyArgs(dst, src []argsKV) []argsKV {
|
---|
372 | if cap(dst) < len(src) {
|
---|
373 | tmp := make([]argsKV, len(src))
|
---|
374 | dst = dst[:cap(dst)] // copy all of dst.
|
---|
375 | copy(tmp, dst)
|
---|
376 | for i := len(dst); i < len(tmp); i++ {
|
---|
377 | // Make sure nothing is nil.
|
---|
378 | tmp[i].key = []byte{}
|
---|
379 | tmp[i].value = []byte{}
|
---|
380 | }
|
---|
381 | dst = tmp
|
---|
382 | }
|
---|
383 | n := len(src)
|
---|
384 | dst = dst[:n]
|
---|
385 | for i := 0; i < n; i++ {
|
---|
386 | dstKV := &dst[i]
|
---|
387 | srcKV := &src[i]
|
---|
388 | dstKV.key = append(dstKV.key[:0], srcKV.key...)
|
---|
389 | if srcKV.noValue {
|
---|
390 | dstKV.value = dstKV.value[:0]
|
---|
391 | } else {
|
---|
392 | dstKV.value = append(dstKV.value[:0], srcKV.value...)
|
---|
393 | }
|
---|
394 | dstKV.noValue = srcKV.noValue
|
---|
395 | }
|
---|
396 | return dst
|
---|
397 | }
|
---|
398 |
|
---|
399 | func delAllArgsBytes(args []argsKV, key []byte) []argsKV {
|
---|
400 | return delAllArgs(args, b2s(key))
|
---|
401 | }
|
---|
402 |
|
---|
403 | func delAllArgs(args []argsKV, key string) []argsKV {
|
---|
404 | for i, n := 0, len(args); i < n; i++ {
|
---|
405 | kv := &args[i]
|
---|
406 | if key == string(kv.key) {
|
---|
407 | tmp := *kv
|
---|
408 | copy(args[i:], args[i+1:])
|
---|
409 | n--
|
---|
410 | i--
|
---|
411 | args[n] = tmp
|
---|
412 | args = args[:n]
|
---|
413 | }
|
---|
414 | }
|
---|
415 | return args
|
---|
416 | }
|
---|
417 |
|
---|
418 | func setArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
|
---|
419 | return setArg(h, b2s(key), b2s(value), noValue)
|
---|
420 | }
|
---|
421 |
|
---|
422 | func setArg(h []argsKV, key, value string, noValue bool) []argsKV {
|
---|
423 | n := len(h)
|
---|
424 | for i := 0; i < n; i++ {
|
---|
425 | kv := &h[i]
|
---|
426 | if key == string(kv.key) {
|
---|
427 | if noValue {
|
---|
428 | kv.value = kv.value[:0]
|
---|
429 | } else {
|
---|
430 | kv.value = append(kv.value[:0], value...)
|
---|
431 | }
|
---|
432 | kv.noValue = noValue
|
---|
433 | return h
|
---|
434 | }
|
---|
435 | }
|
---|
436 | return appendArg(h, key, value, noValue)
|
---|
437 | }
|
---|
438 |
|
---|
439 | func appendArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
|
---|
440 | return appendArg(h, b2s(key), b2s(value), noValue)
|
---|
441 | }
|
---|
442 |
|
---|
443 | func appendArg(args []argsKV, key, value string, noValue bool) []argsKV {
|
---|
444 | var kv *argsKV
|
---|
445 | args, kv = allocArg(args)
|
---|
446 | kv.key = append(kv.key[:0], key...)
|
---|
447 | if noValue {
|
---|
448 | kv.value = kv.value[:0]
|
---|
449 | } else {
|
---|
450 | kv.value = append(kv.value[:0], value...)
|
---|
451 | }
|
---|
452 | kv.noValue = noValue
|
---|
453 | return args
|
---|
454 | }
|
---|
455 |
|
---|
456 | func allocArg(h []argsKV) ([]argsKV, *argsKV) {
|
---|
457 | n := len(h)
|
---|
458 | if cap(h) > n {
|
---|
459 | h = h[:n+1]
|
---|
460 | } else {
|
---|
461 | h = append(h, argsKV{
|
---|
462 | value: []byte{},
|
---|
463 | })
|
---|
464 | }
|
---|
465 | return h, &h[n]
|
---|
466 | }
|
---|
467 |
|
---|
468 | func releaseArg(h []argsKV) []argsKV {
|
---|
469 | return h[:len(h)-1]
|
---|
470 | }
|
---|
471 |
|
---|
472 | func hasArg(h []argsKV, key string) bool {
|
---|
473 | for i, n := 0, len(h); i < n; i++ {
|
---|
474 | kv := &h[i]
|
---|
475 | if key == string(kv.key) {
|
---|
476 | return true
|
---|
477 | }
|
---|
478 | }
|
---|
479 | return false
|
---|
480 | }
|
---|
481 |
|
---|
482 | func peekArgBytes(h []argsKV, k []byte) []byte {
|
---|
483 | for i, n := 0, len(h); i < n; i++ {
|
---|
484 | kv := &h[i]
|
---|
485 | if bytes.Equal(kv.key, k) {
|
---|
486 | return kv.value
|
---|
487 | }
|
---|
488 | }
|
---|
489 | return nil
|
---|
490 | }
|
---|
491 |
|
---|
492 | func peekArgStr(h []argsKV, k string) []byte {
|
---|
493 | for i, n := 0, len(h); i < n; i++ {
|
---|
494 | kv := &h[i]
|
---|
495 | if string(kv.key) == k {
|
---|
496 | return kv.value
|
---|
497 | }
|
---|
498 | }
|
---|
499 | return nil
|
---|
500 | }
|
---|
501 |
|
---|
502 | type argsScanner struct {
|
---|
503 | b []byte
|
---|
504 | }
|
---|
505 |
|
---|
506 | func (s *argsScanner) next(kv *argsKV) bool {
|
---|
507 | if len(s.b) == 0 {
|
---|
508 | return false
|
---|
509 | }
|
---|
510 | kv.noValue = argsHasValue
|
---|
511 |
|
---|
512 | isKey := true
|
---|
513 | k := 0
|
---|
514 | for i, c := range s.b {
|
---|
515 | switch c {
|
---|
516 | case '=':
|
---|
517 | if isKey {
|
---|
518 | isKey = false
|
---|
519 | kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
|
---|
520 | k = i + 1
|
---|
521 | }
|
---|
522 | case '&':
|
---|
523 | if isKey {
|
---|
524 | kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
|
---|
525 | kv.value = kv.value[:0]
|
---|
526 | kv.noValue = argsNoValue
|
---|
527 | } else {
|
---|
528 | kv.value = decodeArgAppend(kv.value[:0], s.b[k:i])
|
---|
529 | }
|
---|
530 | s.b = s.b[i+1:]
|
---|
531 | return true
|
---|
532 | }
|
---|
533 | }
|
---|
534 |
|
---|
535 | if isKey {
|
---|
536 | kv.key = decodeArgAppend(kv.key[:0], s.b)
|
---|
537 | kv.value = kv.value[:0]
|
---|
538 | kv.noValue = argsNoValue
|
---|
539 | } else {
|
---|
540 | kv.value = decodeArgAppend(kv.value[:0], s.b[k:])
|
---|
541 | }
|
---|
542 | s.b = s.b[len(s.b):]
|
---|
543 | return true
|
---|
544 | }
|
---|
545 |
|
---|
546 | func decodeArgAppend(dst, src []byte) []byte {
|
---|
547 | if bytes.IndexByte(src, '%') < 0 && bytes.IndexByte(src, '+') < 0 {
|
---|
548 | // fast path: src doesn't contain encoded chars
|
---|
549 | return append(dst, src...)
|
---|
550 | }
|
---|
551 |
|
---|
552 | // slow path
|
---|
553 | for i := 0; i < len(src); i++ {
|
---|
554 | c := src[i]
|
---|
555 | if c == '%' {
|
---|
556 | if i+2 >= len(src) {
|
---|
557 | return append(dst, src[i:]...)
|
---|
558 | }
|
---|
559 | x2 := hex2intTable[src[i+2]]
|
---|
560 | x1 := hex2intTable[src[i+1]]
|
---|
561 | if x1 == 16 || x2 == 16 {
|
---|
562 | dst = append(dst, '%')
|
---|
563 | } else {
|
---|
564 | dst = append(dst, x1<<4|x2)
|
---|
565 | i += 2
|
---|
566 | }
|
---|
567 | } else if c == '+' {
|
---|
568 | dst = append(dst, ' ')
|
---|
569 | } else {
|
---|
570 | dst = append(dst, c)
|
---|
571 | }
|
---|
572 | }
|
---|
573 | return dst
|
---|
574 | }
|
---|
575 |
|
---|
576 | // decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't
|
---|
577 | // substitute '+' with ' '.
|
---|
578 | //
|
---|
579 | // The function is copy-pasted from decodeArgAppend due to the performance
|
---|
580 | // reasons only.
|
---|
581 | func decodeArgAppendNoPlus(dst, src []byte) []byte {
|
---|
582 | if bytes.IndexByte(src, '%') < 0 {
|
---|
583 | // fast path: src doesn't contain encoded chars
|
---|
584 | return append(dst, src...)
|
---|
585 | }
|
---|
586 |
|
---|
587 | // slow path
|
---|
588 | for i := 0; i < len(src); i++ {
|
---|
589 | c := src[i]
|
---|
590 | if c == '%' {
|
---|
591 | if i+2 >= len(src) {
|
---|
592 | return append(dst, src[i:]...)
|
---|
593 | }
|
---|
594 | x2 := hex2intTable[src[i+2]]
|
---|
595 | x1 := hex2intTable[src[i+1]]
|
---|
596 | if x1 == 16 || x2 == 16 {
|
---|
597 | dst = append(dst, '%')
|
---|
598 | } else {
|
---|
599 | dst = append(dst, x1<<4|x2)
|
---|
600 | i += 2
|
---|
601 | }
|
---|
602 | } else {
|
---|
603 | dst = append(dst, c)
|
---|
604 | }
|
---|
605 | }
|
---|
606 | return dst
|
---|
607 | }
|
---|