github.com/cloudwego/hertz@v0.9.3/pkg/protocol/args.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package protocol 43 44 import ( 45 "bytes" 46 "io" 47 48 "github.com/cloudwego/hertz/internal/bytesconv" 49 "github.com/cloudwego/hertz/internal/nocopy" 50 ) 51 52 const ( 53 argsNoValue = true 54 ArgsHasValue = false 55 ) 56 57 var nilByteSlice = []byte{} 58 59 type argsScanner struct { 60 b []byte 61 } 62 63 type Args struct { 64 noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used 65 66 args []argsKV 67 buf []byte 68 } 69 70 // Set sets 'key=value' argument. 71 func (a *Args) Set(key, value string) { 72 a.args = setArg(a.args, key, value, ArgsHasValue) 73 } 74 75 // Reset clears query args. 76 func (a *Args) Reset() { 77 a.args = a.args[:0] 78 } 79 80 // CopyTo copies all args to dst. 81 func (a *Args) CopyTo(dst *Args) { 82 dst.Reset() 83 dst.args = copyArgs(dst.args, a.args) 84 } 85 86 // Del deletes argument with the given key from query args. 87 func (a *Args) Del(key string) { 88 a.args = delAllArgs(a.args, key) 89 } 90 91 // DelBytes deletes argument with the given key from query args. 92 func (a *Args) DelBytes(key []byte) { 93 a.args = delAllArgs(a.args, bytesconv.B2s(key)) 94 } 95 96 func (s *argsScanner) next(kv *argsKV) bool { 97 if len(s.b) == 0 { 98 return false 99 } 100 kv.noValue = ArgsHasValue 101 102 isKey := true 103 k := 0 104 for i, c := range s.b { 105 switch c { 106 case '=': 107 if isKey { 108 isKey = false 109 kv.key = decodeArgAppend(kv.key[:0], s.b[:i]) 110 k = i + 1 111 } 112 case '&': 113 if isKey { 114 kv.key = decodeArgAppend(kv.key[:0], s.b[:i]) 115 kv.value = kv.value[:0] 116 kv.noValue = argsNoValue 117 } else { 118 kv.value = decodeArgAppend(kv.value[:0], s.b[k:i]) 119 } 120 s.b = s.b[i+1:] 121 return true 122 } 123 } 124 125 if isKey { 126 kv.key = decodeArgAppend(kv.key[:0], s.b) 127 kv.value = kv.value[:0] 128 kv.noValue = argsNoValue 129 } else { 130 kv.value = decodeArgAppend(kv.value[:0], s.b[k:]) 131 } 132 s.b = s.b[len(s.b):] 133 return true 134 } 135 136 func decodeArgAppend(dst, src []byte) []byte { 137 if bytes.IndexByte(src, '%') < 0 && bytes.IndexByte(src, '+') < 0 { 138 // fast path: src doesn't contain encoded chars 139 return append(dst, src...) 140 } 141 142 // slow path 143 for i := 0; i < len(src); i++ { 144 c := src[i] 145 if c == '%' { 146 if i+2 >= len(src) { 147 return append(dst, src[i:]...) 148 } 149 x2 := bytesconv.Hex2intTable[src[i+2]] 150 x1 := bytesconv.Hex2intTable[src[i+1]] 151 if x1 == 16 || x2 == 16 { 152 dst = append(dst, '%') 153 } else { 154 dst = append(dst, x1<<4|x2) 155 i += 2 156 } 157 } else if c == '+' { 158 dst = append(dst, ' ') 159 } else { 160 dst = append(dst, c) 161 } 162 } 163 return dst 164 } 165 166 func allocArg(h []argsKV) ([]argsKV, *argsKV) { 167 n := len(h) 168 if cap(h) > n { 169 h = h[:n+1] 170 } else { 171 h = append(h, argsKV{}) 172 } 173 return h, &h[n] 174 } 175 176 func releaseArg(h []argsKV) []argsKV { 177 return h[:len(h)-1] 178 } 179 180 func updateArgBytes(h []argsKV, key, value []byte) []argsKV { 181 n := len(h) 182 for i := 0; i < n; i++ { 183 kv := &h[i] 184 if kv.noValue && bytes.Equal(key, kv.key) { 185 kv.value = append(kv.value[:0], value...) 186 kv.noValue = ArgsHasValue 187 return h 188 } 189 } 190 return h 191 } 192 193 func setArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV { 194 n := len(h) 195 for i := 0; i < n; i++ { 196 kv := &h[i] 197 if bytes.Equal(key, kv.key) { 198 if noValue { 199 kv.value = kv.value[:0] 200 } else { 201 kv.value = append(kv.value[:0], value...) 202 } 203 kv.noValue = noValue 204 return h 205 } 206 } 207 return appendArgBytes(h, key, value, noValue) 208 } 209 210 func setArg(h []argsKV, key, value string, noValue bool) []argsKV { 211 n := len(h) 212 for i := 0; i < n; i++ { 213 kv := &h[i] 214 if key == string(kv.key) { 215 if noValue { 216 kv.value = kv.value[:0] 217 } else { 218 kv.value = append(kv.value[:0], value...) 219 } 220 kv.noValue = noValue 221 return h 222 } 223 } 224 return appendArg(h, key, value, noValue) 225 } 226 227 func peekArgBytes(h []argsKV, k []byte) []byte { 228 for i, n := 0, len(h); i < n; i++ { 229 kv := &h[i] 230 if bytes.Equal(kv.key, k) { 231 if kv.value != nil { 232 return kv.value 233 } 234 return nilByteSlice 235 } 236 } 237 return nil 238 } 239 240 func peekAllArgBytesToDst(dst [][]byte, h []argsKV, k []byte) [][]byte { 241 for i, n := 0, len(h); i < n; i++ { 242 kv := &h[i] 243 if bytes.Equal(kv.key, k) { 244 dst = append(dst, kv.value) 245 } 246 } 247 return dst 248 } 249 250 func delAllArgsBytes(args []argsKV, key []byte) []argsKV { 251 return delAllArgs(args, bytesconv.B2s(key)) 252 } 253 254 func delAllArgs(args []argsKV, key string) []argsKV { 255 for i, n := 0, len(args); i < n; i++ { 256 kv := &args[i] 257 if key == string(kv.key) { 258 tmp := *kv 259 copy(args[i:], args[i+1:]) 260 n-- 261 i-- 262 args[n] = tmp 263 args = args[:n] 264 } 265 } 266 return args 267 } 268 269 // Has returns true if the given key exists in Args. 270 func (a *Args) Has(key string) bool { 271 return hasArg(a.args, key) 272 } 273 274 func hasArg(h []argsKV, key string) bool { 275 for i, n := 0, len(h); i < n; i++ { 276 kv := &h[i] 277 if key == string(kv.key) { 278 return true 279 } 280 } 281 return false 282 } 283 284 // String returns string representation of query args. 285 func (a *Args) String() string { 286 return string(a.QueryString()) 287 } 288 289 // decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't 290 // substitute '+' with ' '. 291 // 292 // The function is copy-pasted from decodeArgAppend due to the performance 293 // reasons only. 294 func decodeArgAppendNoPlus(dst, src []byte) []byte { 295 if bytes.IndexByte(src, '%') < 0 { 296 // fast path: src doesn't contain encoded chars 297 return append(dst, src...) 298 } 299 300 // slow path 301 for i := 0; i < len(src); i++ { 302 c := src[i] 303 if c == '%' { 304 if i+2 >= len(src) { 305 return append(dst, src[i:]...) 306 } 307 x2 := bytesconv.Hex2intTable[src[i+2]] 308 x1 := bytesconv.Hex2intTable[src[i+1]] 309 if x1 == 16 || x2 == 16 { 310 dst = append(dst, '%') 311 } else { 312 dst = append(dst, x1<<4|x2) 313 i += 2 314 } 315 } else { 316 dst = append(dst, c) 317 } 318 } 319 return dst 320 } 321 322 func peekArgStr(h []argsKV, k string) []byte { 323 for i, n := 0, len(h); i < n; i++ { 324 kv := &h[i] 325 if string(kv.key) == k { 326 return kv.value 327 } 328 } 329 return nil 330 } 331 332 func peekArgStrExists(h []argsKV, k string) (string, bool) { 333 for i, n := 0, len(h); i < n; i++ { 334 kv := &h[i] 335 if string(kv.key) == k { 336 return string(kv.value), true 337 } 338 } 339 return "", false 340 } 341 342 // QueryString returns query string for the args. 343 // 344 // The returned value is valid until the next call to Args methods. 345 func (a *Args) QueryString() []byte { 346 a.buf = a.AppendBytes(a.buf[:0]) 347 return a.buf 348 } 349 350 // ParseBytes parses the given b containing query args. 351 func (a *Args) ParseBytes(b []byte) { 352 a.Reset() 353 354 var s argsScanner 355 s.b = b 356 357 var kv *argsKV 358 a.args, kv = allocArg(a.args) 359 for s.next(kv) { 360 if len(kv.key) > 0 || len(kv.value) > 0 { 361 a.args, kv = allocArg(a.args) 362 } 363 } 364 a.args = releaseArg(a.args) 365 366 if len(a.args) == 0 { 367 return 368 } 369 } 370 371 // Peek returns query arg value for the given key. 372 // 373 // Returned value is valid until the next Args call. 374 func (a *Args) Peek(key string) []byte { 375 return peekArgStr(a.args, key) 376 } 377 378 func (a *Args) PeekExists(key string) (string, bool) { 379 return peekArgStrExists(a.args, key) 380 } 381 382 // PeekAll returns all the arg values for the given key. 383 func (a *Args) PeekAll(key string) [][]byte { 384 var values [][]byte 385 a.VisitAll(func(k, v []byte) { 386 if bytesconv.B2s(k) == key { 387 values = append(values, v) 388 } 389 }) 390 return values 391 } 392 393 func visitArgs(args []argsKV, f func(k, v []byte)) { 394 for i, n := 0, len(args); i < n; i++ { 395 kv := &args[i] 396 f(kv.key, kv.value) 397 } 398 } 399 400 // Len returns the number of query args. 401 func (a *Args) Len() int { 402 return len(a.args) 403 } 404 405 // AppendBytes appends query string to dst and returns the extended dst. 406 func (a *Args) AppendBytes(dst []byte) []byte { 407 for i, n := 0, len(a.args); i < n; i++ { 408 kv := &a.args[i] 409 dst = bytesconv.AppendQuotedArg(dst, kv.key) 410 if !kv.noValue { 411 dst = append(dst, '=') 412 if len(kv.value) > 0 { 413 dst = bytesconv.AppendQuotedArg(dst, kv.value) 414 } 415 } 416 if i+1 < n { 417 dst = append(dst, '&') 418 } 419 } 420 return dst 421 } 422 423 // VisitAll calls f for each existing arg. 424 // 425 // f must not retain references to key and value after returning. 426 // Make key and/or value copies if you need storing them after returning. 427 func (a *Args) VisitAll(f func(key, value []byte)) { 428 visitArgs(a.args, f) 429 } 430 431 // WriteTo writes query string to w. 432 // 433 // WriteTo implements io.WriterTo interface. 434 func (a *Args) WriteTo(w io.Writer) (int64, error) { 435 n, err := w.Write(a.QueryString()) 436 return int64(n), err 437 } 438 439 // Add adds 'key=value' argument. 440 // 441 // Multiple values for the same key may be added. 442 func (a *Args) Add(key, value string) { 443 a.args = appendArg(a.args, key, value, ArgsHasValue) 444 }