package fasthttpimport ()const ( argsNoValue = true argsHasValue = false)// AcquireArgs returns an empty Args object from the pool.//// The returned Args may be returned to the pool with ReleaseArgs// when no longer needed. This allows reducing GC load.func () *Args {returnargsPool.Get().(*Args)}// ReleaseArgs returns the object acquired via AcquireArgs to the pool.//// Do not access the released Args object, otherwise data races may occur.func ( *Args) { .Reset()argsPool.Put()}var argsPool = &sync.Pool{New: func() interface{} {return &Args{} },}// Args represents query arguments.//// It is forbidden copying Args instances. Create new instances instead// and use CopyTo().//// Args instance MUST NOT be used from concurrently running goroutines.typeArgsstruct { noCopy noCopy args []argsKV buf []byte}type argsKV struct { key []byte value []byte noValue bool}// Reset clears query args.func ( *Args) () { .args = .args[:0]}// CopyTo copies all args to dst.func ( *Args) ( *Args) { .args = copyArgs(.args, .args)}// VisitAll calls f for each existing arg.//// f must not retain references to key and value after returning.// Make key and/or value copies if you need storing them after returning.func ( *Args) ( func(, []byte)) {visitArgs(.args, )}// Len returns the number of query args.func ( *Args) () int {returnlen(.args)}// Parse parses the given string containing query args.func ( *Args) ( string) { .buf = append(.buf[:0], ...) .ParseBytes(.buf)}// ParseBytes parses the given b containing query args.func ( *Args) ( []byte) { .Reset()varargsScanner .b = var *argsKV .args, = allocArg(.args)for .next() {iflen(.key) > 0 || len(.value) > 0 { .args, = allocArg(.args) } } .args = releaseArg(.args)}// String returns string representation of query args.func ( *Args) () string {returnstring(.QueryString())}// QueryString returns query string for the args.//// The returned value is valid until the Args is reused or released (ReleaseArgs).// Do not store references to the returned value. Make copies instead.func ( *Args) () []byte { .buf = .AppendBytes(.buf[:0])return .buf}// Sort sorts Args by key and then value using 'f' as comparison function.//// For example args.Sort(bytes.Compare)func ( *Args) ( func(, []byte) int) {sort.SliceStable(.args, func(, int) bool { := (.args[].key, .args[].key)if == 0 {return (.args[].value, .args[].value) == -1 }return == -1 })}// AppendBytes appends query string to dst and returns the extended dst.func ( *Args) ( []byte) []byte {for , := 0, len(.args); < ; ++ { := &.args[] = AppendQuotedArg(, .key)if !.noValue { = append(, '=')iflen(.value) > 0 { = AppendQuotedArg(, .value) } }if +1 < { = append(, '&') } }return}// WriteTo writes query string to w.//// WriteTo implements io.WriterTo interface.func ( *Args) ( io.Writer) (int64, error) { , := .Write(.QueryString())returnint64(), }// Del deletes argument with the given key from query args.func ( *Args) ( string) { .args = delAllArgs(.args, )}// DelBytes deletes argument with the given key from query args.func ( *Args) ( []byte) { .args = delAllArgs(.args, b2s())}// Add adds 'key=value' argument.//// Multiple values for the same key may be added.func ( *Args) (, string) { .args = appendArg(.args, , , argsHasValue)}// AddBytesK adds 'key=value' argument.//// Multiple values for the same key may be added.func ( *Args) ( []byte, string) { .args = appendArg(.args, b2s(), , argsHasValue)}// AddBytesV adds 'key=value' argument.//// Multiple values for the same key may be added.func ( *Args) ( string, []byte) { .args = appendArg(.args, , b2s(), argsHasValue)}// AddBytesKV adds 'key=value' argument.//// Multiple values for the same key may be added.func ( *Args) (, []byte) { .args = appendArg(.args, b2s(), b2s(), argsHasValue)}// AddNoValue adds only 'key' as argument without the '='.//// Multiple values for the same key may be added.func ( *Args) ( string) { .args = appendArg(.args, , "", argsNoValue)}// AddBytesKNoValue adds only 'key' as argument without the '='.//// Multiple values for the same key may be added.func ( *Args) ( []byte) { .args = appendArg(.args, b2s(), "", argsNoValue)}// Set sets 'key=value' argument.func ( *Args) (, string) { .args = setArg(.args, , , argsHasValue)}// SetBytesK sets 'key=value' argument.func ( *Args) ( []byte, string) { .args = setArg(.args, b2s(), , argsHasValue)}// SetBytesV sets 'key=value' argument.func ( *Args) ( string, []byte) { .args = setArg(.args, , b2s(), argsHasValue)}// SetBytesKV sets 'key=value' argument.func ( *Args) (, []byte) { .args = setArgBytes(.args, , , argsHasValue)}// SetNoValue sets only 'key' as argument without the '='.//// Only key in argument, like key1&key2func ( *Args) ( string) { .args = setArg(.args, , "", argsNoValue)}// SetBytesKNoValue sets 'key' argument.func ( *Args) ( []byte) { .args = setArg(.args, b2s(), "", argsNoValue)}// Peek returns query arg value for the given key.//// The returned value is valid until the Args is reused or released (ReleaseArgs).// Do not store references to the returned value. Make copies instead.func ( *Args) ( string) []byte {returnpeekArgStr(.args, )}// PeekBytes returns query arg value for the given key.//// The returned value is valid until the Args is reused or released (ReleaseArgs).// Do not store references to the returned value. Make copies instead.func ( *Args) ( []byte) []byte {returnpeekArgBytes(.args, )}// PeekMulti returns all the arg values for the given key.func ( *Args) ( string) [][]byte {var [][]byte .VisitAll(func(, []byte) {ifstring() == { = append(, ) } })return}// PeekMultiBytes returns all the arg values for the given key.func ( *Args) ( []byte) [][]byte {return .PeekMulti(b2s())}// Has returns true if the given key exists in Args.func ( *Args) ( string) bool {returnhasArg(.args, )}// HasBytes returns true if the given key exists in Args.func ( *Args) ( []byte) bool {returnhasArg(.args, b2s())}// ErrNoArgValue is returned when Args value with the given key is missing.varErrNoArgValue = errors.New("no Args value for the given key")// GetUint returns uint value for the given key.func ( *Args) ( string) (int, error) { := .Peek()iflen() == 0 {return -1, ErrNoArgValue }returnParseUint()}// SetUint sets uint value for the given key.func ( *Args) ( string, int) { := bytebufferpool.Get() .B = AppendUint(.B[:0], ) .SetBytesV(, .B)bytebufferpool.Put()}// SetUintBytes sets uint value for the given key.func ( *Args) ( []byte, int) { .SetUint(b2s(), )}// GetUintOrZero returns uint value for the given key.//// Zero (0) is returned on error.func ( *Args) ( string) int { , := .GetUint()if != nil { = 0 }return}// GetUfloat returns ufloat value for the given key.func ( *Args) ( string) (float64, error) { := .Peek()iflen() == 0 {return -1, ErrNoArgValue }returnParseUfloat()}// GetUfloatOrZero returns ufloat value for the given key.//// Zero (0) is returned on error.func ( *Args) ( string) float64 { , := .GetUfloat()if != nil { = 0 }return}// GetBool returns boolean value for the given key.//// true is returned for "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes",// otherwise false is returned.func ( *Args) ( string) bool {switchstring(.Peek()) {// Support the same true cases as strconv.ParseBool // See: https://github.com/golang/go/blob/4e1b11e2c9bdb0ddea1141eed487be1a626ff5be/src/strconv/atob.go#L12 // and Y and Yes versions.case"1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes":returntruedefault:returnfalse }}func visitArgs( []argsKV, func(, []byte)) {for , := 0, len(); < ; ++ { := &[] (.key, .value) }}func visitArgsKey( []argsKV, func( []byte)) {for , := 0, len(); < ; ++ { := &[] (.key) }}func copyArgs(, []argsKV) []argsKV {ifcap() < len() { := make([]argsKV, len()) := len() = [:cap()] // copy all of dst.copy(, )for := ; < len(); ++ {// Make sure nothing is nil. [].key = []byte{} [].value = []byte{} } = } := len() = [:]for := 0; < ; ++ { := &[] := &[] .key = append(.key[:0], .key...)if .noValue { .value = .value[:0] } else { .value = append(.value[:0], .value...) } .noValue = .noValue }return}func delAllArgsBytes( []argsKV, []byte) []argsKV {returndelAllArgs(, b2s())}func delAllArgs( []argsKV, string) []argsKV {for , := 0, len(); < ; ++ { := &[]if == string(.key) { := *copy([:], [+1:]) -- -- [] = = [:] } }return}func setArgBytes( []argsKV, , []byte, bool) []argsKV {returnsetArg(, b2s(), b2s(), )}func setArg( []argsKV, , string, bool) []argsKV { := len()for := 0; < ; ++ { := &[]if == string(.key) {if { .value = .value[:0] } else { .value = append(.value[:0], ...) } .noValue = return } }returnappendArg(, , , )}func appendArgBytes( []argsKV, , []byte, bool) []argsKV {returnappendArg(, b2s(), b2s(), )}func appendArg( []argsKV, , string, bool) []argsKV {var *argsKV , = allocArg() .key = append(.key[:0], ...)if { .value = .value[:0] } else { .value = append(.value[:0], ...) } .noValue = return}func allocArg( []argsKV) ([]argsKV, *argsKV) { := len()ifcap() > { = [:+1] } else { = append(, argsKV{value: []byte{}, }) }return , &[]}func releaseArg( []argsKV) []argsKV {return [:len()-1]}func hasArg( []argsKV, string) bool {for , := 0, len(); < ; ++ { := &[]if == string(.key) {returntrue } }returnfalse}func peekArgBytes( []argsKV, []byte) []byte {for , := 0, len(); < ; ++ { := &[]ifbytes.Equal(.key, ) {return .value } }returnnil}func peekArgStr( []argsKV, string) []byte {for , := 0, len(); < ; ++ { := &[]ifstring(.key) == {return .value } }returnnil}type argsScanner struct { b []byte}func ( *argsScanner) ( *argsKV) bool {iflen(.b) == 0 {returnfalse } .noValue = argsHasValue := true := 0for , := range .b {switch {case'=':if { = false .key = decodeArgAppend(.key[:0], .b[:]) = + 1 }case'&':if { .key = decodeArgAppend(.key[:0], .b[:]) .value = .value[:0] .noValue = argsNoValue } else { .value = decodeArgAppend(.value[:0], .b[:]) } .b = .b[+1:]returntrue } }if { .key = decodeArgAppend(.key[:0], .b) .value = .value[:0] .noValue = argsNoValue } else { .value = decodeArgAppend(.value[:0], .b[:]) } .b = .b[len(.b):]returntrue}func decodeArgAppend(, []byte) []byte { := bytes.IndexByte(, '%') := bytes.IndexByte(, '+')if == -1 && == -1 {// fast path: src doesn't contain encoded charsreturnappend(, ...) } := 0switch {case == -1: = case == -1: = case > : = default: = } = append(, [:]...)// slow pathfor := ; < len(); ++ { := []switch {case'%':if +2 >= len() {returnappend(, [:]...) } := hex2intTable[[+2]] := hex2intTable[[+1]]if == 16 || == 16 { = append(, '%') } else { = append(, <<4|) += 2 }case'+': = append(, ' ')default: = append(, ) } }return}// decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't// substitute '+' with ' './/// The function is copy-pasted from decodeArgAppend due to the performance// reasons only.func decodeArgAppendNoPlus(, []byte) []byte { := bytes.IndexByte(, '%')if < 0 {// fast path: src doesn't contain encoded charsreturnappend(, ...) } = append(, [:]...)// slow pathfor := ; < len(); ++ { := []if == '%' {if +2 >= len() {returnappend(, [:]...) } := hex2intTable[[+2]] := hex2intTable[[+1]]if == 16 || == 16 { = append(, '%') } else { = append(, <<4|) += 2 } } else { = append(, ) } }return}func peekAllArgBytesToDst( [][]byte, []argsKV, []byte) [][]byte {for , := 0, len(); < ; ++ { := &[]ifbytes.Equal(.key, ) { = append(, .value) } }return}func peekArgsKeys( [][]byte, []argsKV) [][]byte {for , := 0, len(); < ; ++ { := &[] = append(, .key) }return}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.