diff --git a/src/go.mod b/src/go.mod index 77f1af98..bed561e9 100644 --- a/src/go.mod +++ b/src/go.mod @@ -7,7 +7,7 @@ require ( github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef github.com/dechristopher/octad/v2 v2.1.0 github.com/go-webauthn/webauthn v0.17.4 - github.com/gofiber/contrib/v3/websocket v1.2.1 + github.com/gofiber/contrib/v3/websocket v1.2.2 github.com/gofiber/fiber/v3 v3.4.0 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.10.0 @@ -21,7 +21,7 @@ require ( github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef github.com/valyala/fastjson v1.6.10 - golang.org/x/crypto v0.53.0 + golang.org/x/crypto v0.54.0 golang.org/x/image v0.44.0 golang.org/x/sync v0.22.0 golang.org/x/text v0.40.0 @@ -36,19 +36,19 @@ require ( github.com/fxamacker/cbor/v2 v2.9.2 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/go-webauthn/x v0.2.6 // indirect - github.com/gofiber/schema v1.8.0 // indirect - github.com/gofiber/utils/v2 v2.1.1 // indirect + github.com/gofiber/schema v1.8.3 // indirect + github.com/gofiber/utils/v2 v2.2.0 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/go-tpm v0.9.8 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/klauspost/compress v1.19.0 // indirect + github.com/klauspost/compress v1.19.1 // indirect github.com/klauspost/cpuid/v2 v2.4.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.15 // indirect - github.com/mattn/go-isatty v0.0.22 // indirect + github.com/mattn/go-isatty v0.0.23 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect @@ -64,7 +64,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.56.0 // indirect - golang.org/x/sys v0.46.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/ini.v1 v1.67.3 // indirect ) diff --git a/src/go.sum b/src/go.sum index 55fb935b..e3c95ece 100644 --- a/src/go.sum +++ b/src/go.sum @@ -33,12 +33,18 @@ github.com/go-webauthn/x v0.2.6 h1:TEyDuQAIiEgYpx60nKiBJIX/5nSUC8LxNbH+uf5U9uk= github.com/go-webauthn/x v0.2.6/go.mod h1:45bA7YEqyQhRcQJ/TiBb46Ww8yqHBGvgEhQ3WWF0aDo= github.com/gofiber/contrib/v3/websocket v1.2.1 h1:E4I1dVCvNF56ELdKksUJilo1r7dpw7lgt1DmsJd7hOo= github.com/gofiber/contrib/v3/websocket v1.2.1/go.mod h1:rQO2z0SbC/l8lP36gXTKrD1QZBXzHKwt9E8c5em1IiA= +github.com/gofiber/contrib/v3/websocket v1.2.2 h1:kmoD+A0f4btoDSOnZJUHzJyBi1tQqL9tNhn4n0nPJSQ= +github.com/gofiber/contrib/v3/websocket v1.2.2/go.mod h1:H0kwFA9iXc5RJca4QqCjWwc2kIpuOGQSZJONqsxwWXI= github.com/gofiber/fiber/v3 v3.4.0 h1:F0aND4vwZF7dR7cbvSwFQQEpBU902XHKWxrLsFBkVqw= github.com/gofiber/fiber/v3 v3.4.0/go.mod h1:nAhJfdxUIJJph2tPWPmqWf8QDIN2iiqQiQf3lENZpdk= github.com/gofiber/schema v1.8.0 h1:NGsC9toPHmj8Xg4KpznuXBzNmHG6V5YV0tXKpKMcmis= github.com/gofiber/schema v1.8.0/go.mod h1:lmbXPQ8hvzXSLkdS2DS7pb4kpunC2Roh7Sj3HMjGfzA= +github.com/gofiber/schema v1.8.3 h1:06ZedxIYjngzc0095PYy7uWnFnbRflWFpikvZH61fDc= +github.com/gofiber/schema v1.8.3/go.mod h1:jWnnZdhcW1mHyV+VnfRxKJDPNcepJsTZ9RIWxrr32Ng= github.com/gofiber/utils/v2 v2.1.1 h1:kGnoGjwEnFW6w0x45W+kLlmMJvqBGkuUA4oMWKn/T/I= github.com/gofiber/utils/v2 v2.1.1/go.mod h1:DdOgEVwQTi8cou/AKWPqhXOR4fHGRVhA/rEWL3IXG7Q= +github.com/gofiber/utils/v2 v2.2.0 h1:YSSmCzQponq/f9uSOg2HtXC5qK1Dmor0o6DqaQVz8GE= +github.com/gofiber/utils/v2 v2.2.0/go.mod h1:Ieopk6sQh7rbhQ12aBNCJtJuG0gxAg0nz63sFCrrOmE= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -61,6 +67,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ= github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= +github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= @@ -76,6 +84,8 @@ github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= +github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= @@ -152,14 +162,20 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/src/vendor/github.com/gofiber/schema/.gitignore b/src/vendor/github.com/gofiber/schema/.gitignore index 0cafd84a..67d029be 100644 --- a/src/vendor/github.com/gofiber/schema/.gitignore +++ b/src/vendor/github.com/gofiber/schema/.gitignore @@ -2,3 +2,6 @@ coverage.coverprofile vendor .idea + +# Binaries +*.test diff --git a/src/vendor/github.com/gofiber/schema/README.md b/src/vendor/github.com/gofiber/schema/README.md index 5bc767d7..649cf7cd 100644 --- a/src/vendor/github.com/gofiber/schema/README.md +++ b/src/vendor/github.com/gofiber/schema/README.md @@ -2,6 +2,8 @@ Package gofiber/schema converts structs to and from form values. +Per-commit benchmark charts: + ## Example Here's a quick example: we parse POST form values and then decode them into a struct: @@ -111,12 +113,13 @@ The `default` tag option is supported for the following types: Fiber is an open-source project that runs on donations to pay the bills, e.g., our domain name, hosting, and serverless infrastructure. If you want to support Fiber, please become a [GitHub Sponsor](https://github.com/sponsors/gofiber). + ### 📅 Monthly Sponsors - +
🔥 Fiber Guardian@coderabbitai
☕ Fiber Supporter@NdoleStudio @petercool
🪴 Fiber Friend@bsdrop
🪴 Fiber Friend@simonheisstpeter
### 🎁 One-time Sponsors diff --git a/src/vendor/github.com/gofiber/schema/cache.go b/src/vendor/github.com/gofiber/schema/cache.go index df00d9b6..82866999 100644 --- a/src/vendor/github.com/gofiber/schema/cache.go +++ b/src/vendor/github.com/gofiber/schema/cache.go @@ -6,12 +6,15 @@ package schema import ( "errors" + "maps" "reflect" "strings" "sync" + "sync/atomic" utils "github.com/gofiber/utils/v2" utilstrings "github.com/gofiber/utils/v2/strings" + "github.com/gofiber/utils/v2/swar" ) const maxParserIndex = 1000 @@ -24,32 +27,45 @@ var ( // newCache returns a new cache. func newCache() *cache { c := cache{ - m: make(map[reflect.Type]*structInfo), - pathCache: make(map[pathCacheKey][]pathPart), - regconv: make(map[reflect.Type]Converter), - tag: "schema", + tag: "schema", } return &c } // cache caches meta-data about a struct. type cache struct { - l sync.RWMutex - m map[reflect.Type]*structInfo - pathCache map[pathCacheKey][]pathPart - regconv map[reflect.Type]Converter - tag string + l sync.RWMutex // serializes configuration writes (tag, regconv) + m sync.Map // map[reflect.Type]cacheEntry + // regconv holds the registered converters as an immutable map published + // atomically: registerConverter replaces the whole map (copy-on-write + // under l), so readers never touch a map that is being written. + regconv atomic.Pointer[map[reflect.Type]Converter] + tag string + // gen is bumped (under l) before m is cleared on configuration changes; + // cached entries are tagged with the generation they were built under + // and ignored on mismatch, so any call starting after a reconfiguration + // returns observes the new configuration even if a racing build stored + // a stale entry after the clear. + gen atomic.Uint64 } -type pathCacheKey struct { - path string - typ reflect.Type +// cacheEntry tags a structInfo with the configuration generation it was +// built under; entries are stored by pointer in c.m (matching the encoder's +// encPlan pattern). +type cacheEntry struct { + info *structInfo + gen uint64 } // registerConverter registers a converter function for a custom type. func (c *cache) registerConverter(value interface{}, converterFunc Converter) { c.l.Lock() - c.regconv[reflect.TypeOf(value)] = converterFunc + next := make(map[reflect.Type]Converter) + if prev := c.regconv.Load(); prev != nil { + maps.Copy(next, *prev) + } + next[reflect.TypeOf(value)] = converterFunc + c.regconv.Store(&next) c.reset() c.l.Unlock() } @@ -61,26 +77,28 @@ func (c *cache) registerConverter(value interface{}, converterFunc Converter) { // reflect.Value.FieldByString(). Multiple parts are required for slices of // structs. func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { - cacheKey := pathCacheKey{path: p, typ: t} - c.l.RLock() - cached, ok := c.pathCache[cacheKey] - c.l.RUnlock() - if ok { - return cached, nil + if t.Kind() != reflect.Struct { + return nil, errInvalidPath } + return c.parsePathInfo(p, c.get(t)) +} - var struc *structInfo +// parsePathInfo is parsePath with the root struct's info already resolved, +// letting Decode look it up once per call instead of once per key. The +// parsed-path cache lives on that structInfo, keyed by the plain path +// string, which hashes much cheaper than a composite key. +func (c *cache) parsePathInfo(p string, rootInfo *structInfo) ([]pathPart, error) { + if cached, ok := rootInfo.paths.Load(p); ok { + return cached.([]pathPart), nil + } + + struc := rootInfo + var t reflect.Type var field *fieldInfo var index64 int64 var parts []pathPart - var path []string + var hops []pathHop for keyStart := 0; ; { - if t.Kind() != reflect.Struct { - return nil, errInvalidPath - } - if struc = c.get(t); struc == nil { - return nil, errInvalidPath - } keyEnd, segment, err := nextPathSegment(p, keyStart) if err != nil { return nil, errInvalidPath @@ -88,9 +106,11 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { if field = struc.get(segment); field == nil { return nil, errInvalidPath } - // Valid field. Append index. - path = append(path, field.name) - if field.isSliceOfStructs && !isMultipartField(field.typ) && (!field.unmarshalerInfo.IsValid || (field.unmarshalerInfo.IsValid && field.unmarshalerInfo.IsSliceElement)) { + // Valid field. Append the hop; the field's index chain was resolved + // when the structInfo was built, so the decoder walks plain indices + // instead of repeating FieldByName lookups on every Decode call. + hops = append(hops, pathHop{index: field.index, ensure: struc.anonymousPtrFields}) + if field.isSliceOfStructs && !field.isMultipart && (!field.unmarshalerInfo.IsValid || (field.unmarshalerInfo.IsValid && field.unmarshalerInfo.IsSliceElement)) { // Parse a special case: slices of structs. // i+1 must be the slice index. // @@ -113,11 +133,11 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { return nil, errIndexTooLarge } parts = append(parts, pathPart{ - path: path, + hops: hops, field: field, index: int(index64), }) - path = nil + hops = nil // Get the next struct type, dropping ptrs. if field.typ.Kind() == reflect.Ptr { @@ -144,27 +164,44 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { if keyStart >= len(p) { return nil, errInvalidPath } + if t.Kind() != reflect.Struct { + return nil, errInvalidPath + } + struc = c.get(t) } - // Add the remaining. + // Add the remaining. A part without hops means the path terminated at a + // slice index ("a.0"), so the decoder receives a slice element there. parts = append(parts, pathPart{ - path: path, + hops: hops, field: field, index: -1, + elem: len(hops) == 0, }) - c.l.Lock() - if cached, ok := c.pathCache[cacheKey]; ok { - c.l.Unlock() - return cached, nil + // Detach the key: callers may pass strings aliasing reused request buffers. + if cached, loaded := rootInfo.paths.LoadOrStore(strings.Clone(p), parts); loaded { + return cached.([]pathPart), nil } - c.pathCache[cacheKey] = parts - c.l.Unlock() return parts, nil } +// dotBroadcast is the SWAR needle for '.'; hoisted so the word loop in +// nextPathSegment pays no per-call broadcast cost. +var dotBroadcast = swar.Broadcast('.') + func nextPathSegment(path string, start int) (int, string, error) { end := start + for end+swar.WordLen <= len(path) { + if m := swar.ZeroLanes(swar.Load8(path, end) ^ dotBroadcast); m != 0 { + end += swar.FirstLane(m) + if start == end { + return 0, "", errInvalidPath + } + return end, path[start:end], nil + } + end += swar.WordLen + } for end < len(path) && path[end] != '.' { end++ } @@ -176,37 +213,66 @@ func nextPathSegment(path string, start int) (int, string, error) { // get returns a cached structInfo, creating it if necessary. func (c *cache) get(t reflect.Type) *structInfo { - c.l.RLock() - info := c.m[t] - c.l.RUnlock() - if info == nil { - info = c.create(t, "") - c.l.Lock() - c.m[t] = info - c.l.Unlock() + gen := c.gen.Load() + if v, ok := c.m.Load(t); ok { + // Ignore entries built under an older configuration: a build racing + // a reconfiguration may store one after the clear. Hit-validation + // guarantees that any call starting after the reconfiguration + // returned observes the new configuration (a call already in flight + // during the reconfiguration may still briefly use old metadata, + // which is inherent to concurrent reconfiguration). + if e := v.(*cacheEntry); e.gen == gen { + return e.info + } } + info := c.create(t, "") + if c.gen.Load() == gen { + c.m.Store(t, &cacheEntry{info: info, gen: gen}) + } + // If the configuration changed while building, serve the result once + // without caching it (or with a stale tag that hit-validation ignores); + // the next call rebuilds fresh. return info } -// reset clears cached metadata and must be called with c.l held. +// reset clears cached metadata and must be called with c.l held. Parsed +// path caches live on the structInfos, so dropping them drops those too. func (c *cache) reset() { - c.m = make(map[reflect.Type]*structInfo) - c.pathCache = make(map[pathCacheKey][]pathPart) + c.gen.Add(1) + c.m.Clear() +} + +// aliasTag returns the configured tag name under the configuration lock, so +// metadata builds racing SetAliasTag read a consistent value. +func (c *cache) aliasTag() string { + c.l.RLock() + tag := c.tag + c.l.RUnlock() + return tag } // create creates a structInfo with meta-data about a struct. func (c *cache) create(t reflect.Type, parentAlias string) *structInfo { + // Snapshot the alias tag once so every field of this type is analyzed + // under a consistent configuration and we don't churn the config lock + // per field. + tag := c.aliasTag() info := &structInfo{} var anonymousInfos []*structInfo + var anonymousIdx [][]int for i := 0; i < t.NumField(); i++ { structField := t.Field(i) - if structField.Anonymous && structField.Type.Kind() == reflect.Ptr { + // Only exported anonymous pointers can be allocated; unexported ones + // are not settable and Set would panic. + if structField.Anonymous && structField.Type.Kind() == reflect.Ptr && structField.IsExported() { info.anonymousPtrFields = append(info.anonymousPtrFields, i) } - if f := c.createField(structField, parentAlias); f != nil { + if f := c.createField(structField, parentAlias, tag); f != nil { + f.index = structField.Index info.fields = append(info.fields, f) if ft := indirectType(f.typ); ft.Kind() == reflect.Struct && f.isAnonymous { anonymousInfos = append(anonymousInfos, c.create(ft, f.canonicalAlias)) + anonymousIdx = append(anonymousIdx, structField.Index) } } } @@ -216,7 +282,12 @@ func (c *cache) create(t reflect.Type, parentAlias string) *structInfo { others = append(others, anonymousInfos[i+1:]...) for _, f := range a.fields { if !containsAlias(others, f.alias) { - info.fields = append(info.fields, f) + // Copy the promoted field so its index chain can be prefixed + // with the embedded field's index; the original stays valid + // for the embedded type's own structInfo. + pf := *f + pf.index = append(append(make([]int, 0, len(anonymousIdx[i])+len(f.index)), anonymousIdx[i]...), f.index...) + info.fields = append(info.fields, &pf) } } } @@ -227,12 +298,51 @@ func (c *cache) create(t reflect.Type, parentAlias string) *structInfo { } } info.requiredFields = c.buildRequiredFields(info) + // The setDefaults walk also allocates nil anonymous embedded pointers, + // so it can only be skipped when neither defaults nor such pointers + // exist anywhere in the tree. + info.needsDefaultsWalk = c.needsDefaultsWalk(t, tag, map[reflect.Type]bool{}) return info } -// createField creates a fieldInfo for the given field. -func (c *cache) createField(field reflect.StructField, parentAlias string) *fieldInfo { - alias, options := fieldAlias(field, c.tag) +// needsDefaultsWalk reports whether the setDefaults walk can have any effect +// on the struct tree rooted at t: it declares a default tag option, or has an +// (exported) anonymous pointer field the walk allocates, anywhere in the +// tree. visited guards against recursive types. tag is the alias tag +// snapshot for this build, so no per-field config lock is taken. +func (c *cache) needsDefaultsWalk(t reflect.Type, tag string, visited map[reflect.Type]bool) bool { + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct || visited[t] { + return false + } + visited[t] = true + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + // Mirror the anonymousPtrFields filter: only exported anonymous + // pointers are ever allocated, so only they justify the walk. + if field.Anonymous && field.Type.Kind() == reflect.Ptr && field.IsExported() { + return true + } + alias, options := fieldAlias(field, tag) + if alias == "-" { + continue + } + if options.getDefaultOptionValue() != "" { + return true + } + if c.needsDefaultsWalk(field.Type, tag, visited) { + return true + } + } + return false +} + +// createField creates a fieldInfo for the given field. tag is the alias tag +// snapshot for the enclosing type's build. +func (c *cache) createField(field reflect.StructField, parentAlias, tag string) *fieldInfo { + alias, options := fieldAlias(field, tag) if alias == "-" { // Ignore this field. return nil @@ -268,6 +378,18 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel } } + // Reuse the unmarshaler facts when the successive type unwrappings land + // on the same type (the common non-pointer, non-slice case). + derefT := indirectType(field.Type) + derefU := m + if derefT != field.Type { + derefU = isTextUnmarshaler(reflect.Zero(derefT)) + } + elemU := derefU + if ft != derefT { + elemU = isTextUnmarshaler(reflect.Zero(ft)) + } + return &fieldInfo{ typ: field.Type, name: field.Name, @@ -275,6 +397,9 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel aliasLower: utilstrings.ToLower(alias), canonicalAlias: canonicalAlias, unmarshalerInfo: m, + derefUnmarshaler: derefU, + elemUnmarshaler: elemU, + isMultipart: isMultipartField(field.Type), isSliceOfStructs: isSlice && isStruct, isAnonymous: field.Anonymous, isRequired: options.Contains("required"), @@ -284,7 +409,11 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel // converter returns the converter for a type. func (c *cache) converter(t reflect.Type) Converter { - return c.regconv[t] + reg := c.regconv.Load() + if reg == nil { + return nil + } + return (*reg)[t] } // ---------------------------------------------------------------------------- @@ -294,6 +423,15 @@ type structInfo struct { fieldsByName map[string]*fieldInfo anonymousPtrFields []int requiredFields map[string][]fieldWithPrefix + // paths caches parsed paths rooted at this struct type + // (map[string][]pathPart); keys are cloned so they never alias reused + // request buffers. + paths sync.Map + // needsDefaultsWalk reports whether the setDefaults walk can have any + // effect on this struct tree: it is set when a default tag option or an + // anonymous embedded pointer field (which the walk allocates) exists + // anywhere in the tree, letting the decoder skip the walk otherwise. + needsDefaultsWalk bool } func (i *structInfo) get(alias string) *fieldInfo { @@ -314,18 +452,15 @@ func (c *cache) buildRequiredFields(info *structInfo) map[string][]fieldWithPref for key, fields := range nested.requiredFields { requiredKey := field.canonicalAlias + "." + key for _, nestedField := range fields { - requiredFields = appendRequiredField(requiredFields, requiredKey, fieldWithPrefix{ - fieldInfo: nestedField.fieldInfo, - prefix: nestedPrefix + nestedField.prefix, - }) + requiredFields = appendRequiredField(requiredFields, requiredKey, + newFieldWithPrefix(nestedField.fieldInfo, nestedPrefix+nestedField.prefix)) } } } } if field.isRequired { - requiredFields = appendRequiredField(requiredFields, field.canonicalAlias, fieldWithPrefix{ - fieldInfo: field, - }) + requiredFields = appendRequiredField(requiredFields, field.canonicalAlias, + newFieldWithPrefix(field, "")) } } return requiredFields @@ -343,6 +478,10 @@ func containsAlias(infos []*structInfo, alias string) bool { type fieldInfo struct { typ reflect.Type + // index is the field index chain relative to the struct type whose + // structInfo holds this fieldInfo; promoted fields carry the full chain + // through the embedded structs (a copy is made per promotion level). + index []int // name is the field name in the struct. name string alias string @@ -357,6 +496,20 @@ type fieldInfo struct { // unmarshalerInfo contains information regarding the // encoding.TextUnmarshaler implementation of the field type. unmarshalerInfo unmarshaler + // derefUnmarshaler caches the encoding.TextUnmarshaler information for + // the field type after one pointer dereference, which is what the + // decoder sees after walking to the field. Only the type-level flags are + // meaningful; the decoder binds instances to live values itself. + derefUnmarshaler unmarshaler + // elemUnmarshaler is like derefUnmarshaler but for the fully unwrapped + // slice element type; the decoder uses it when a path terminates at a + // slice index (e.g. "a.0") and the value at hand is an element rather + // than the slice field itself. + elemUnmarshaler unmarshaler + // isMultipart indicates whether the field type is one of the supported + // multipart file header shapes, precomputed so the decoder can skip the + // type comparisons on every other field. + isMultipart bool // isSliceOfStructs indicates if the field type is a slice of structs. isSliceOfStructs bool // isAnonymous indicates whether the field is embedded in the struct. @@ -374,8 +527,22 @@ func (f *fieldInfo) paths(prefix string) []string { type pathPart struct { field *fieldInfo - path []string // path to the field: walks structs using field names. - index int // struct index in slices of structs. + hops []pathHop // path to the field: walks structs using field indices. + index int // struct index in slices of structs. + // elem marks a terminal part whose path ended at a slice index ("a.0"): + // the decoder's value is then an element of the slice field rather than + // the field itself. + elem bool +} + +// pathHop describes one named-field lookup along a path. index is the field +// index chain relative to the struct at this level (more than one element +// when the field is promoted from embedded structs), and ensure lists the +// anonymous pointer fields of that struct which must be allocated before the +// walk so promoted fields stay reachable. +type pathHop struct { + index []int + ensure []int } // ---------------------------------------------------------------------------- @@ -399,21 +566,26 @@ func fieldAlias(field reflect.StructField, tagName string) (alias string, option } // tagOptions is the string following a comma in a struct field's tag, or -// the empty string. It does not include the leading comma. -type tagOptions []string +// the empty string. It does not include the leading comma. Keeping the raw +// comma-separated string avoids the []string allocation of strings.Split on +// hot paths (the encoder parses tags on every Encode call). +type tagOptions string // parseTag splits a struct field's url tag into its name and comma-separated // options. func parseTag(tag string) (string, tagOptions) { if idx := strings.IndexByte(tag, ','); idx != -1 { - return tag[:idx], strings.Split(tag[idx+1:], ",") + return tag[:idx], tagOptions(tag[idx+1:]) } - return tag, nil + return tag, "" } // Contains checks whether the tagOptions contains the specified option. func (o tagOptions) Contains(option string) bool { - for _, s := range o { + if o == "" { + return false + } + for s := range strings.SplitSeq(string(o), ",") { if s == option { return true } @@ -422,7 +594,10 @@ func (o tagOptions) Contains(option string) bool { } func (o tagOptions) getDefaultOptionValue() string { - for _, s := range o { + if o == "" { + return "" + } + for s := range strings.SplitSeq(string(o), ",") { if value, ok := strings.CutPrefix(s, "default:"); ok { return value } diff --git a/src/vendor/github.com/gofiber/schema/converter.go b/src/vendor/github.com/gofiber/schema/converter.go index 0162c7d0..27aa76bb 100644 --- a/src/vendor/github.com/gofiber/schema/converter.go +++ b/src/vendor/github.com/gofiber/schema/converter.go @@ -89,8 +89,14 @@ func convertFloat64(value string) reflect.Value { return invalidValue } +// Native int/uint parsing goes through utils.ParseInt/ParseUint (which have +// a SWAR fast path) with an inline native-fit guard: `int64(int(v)) == v` +// compiles away on 64-bit and rejects values on 32-bit that a plain int() +// conversion would silently truncate. The guard is written inline at each +// call site so the ~8ns parse doesn't pay a wrapper call frame. + func convertInt(value string) reflect.Value { - if v, err := utils.ParseInt(value); err == nil { + if v, err := utils.ParseInt(value); err == nil && int64(int(v)) == v { return reflect.ValueOf(int(v)) } return invalidValue @@ -129,7 +135,7 @@ func convertString(value string) reflect.Value { } func convertUint(value string) reflect.Value { - if v, err := utils.ParseUint(value); err == nil { + if v, err := utils.ParseUint(value); err == nil && uint64(uint(v)) == v { return reflect.ValueOf(uint(v)) } return invalidValue @@ -163,56 +169,98 @@ func convertUint64(value string) reflect.Value { return invalidValue } -func convertPointer(k reflect.Kind, value string) reflect.Value { - conv := getBuiltinConverter(k) - if conv == nil { - return invalidValue - } - if v := conv(value); v.IsValid() { - switch k { - case boolType: - converted := v.Bool() - return reflect.ValueOf(&converted) - case float32Type: - converted := float32(v.Float()) - return reflect.ValueOf(&converted) - case float64Type: - converted := v.Float() - return reflect.ValueOf(&converted) - case intType: - converted := int(v.Int()) - return reflect.ValueOf(&converted) - case int8Type: - converted := int8(v.Int()) - return reflect.ValueOf(&converted) - case int16Type: - converted := int16(v.Int()) - return reflect.ValueOf(&converted) - case int32Type: - converted := int32(v.Int()) - return reflect.ValueOf(&converted) - case int64Type: - converted := v.Int() - return reflect.ValueOf(&converted) - case stringType: - converted := v.String() - return reflect.ValueOf(&converted) - case uintType: - converted := uint(v.Uint()) - return reflect.ValueOf(&converted) - case uint8Type: - converted := uint8(v.Uint()) - return reflect.ValueOf(&converted) - case uint16Type: - converted := uint16(v.Uint()) - return reflect.ValueOf(&converted) - case uint32Type: - converted := uint32(v.Uint()) - return reflect.ValueOf(&converted) - case uint64Type: - converted := v.Uint() - return reflect.ValueOf(&converted) +// setBuiltinKind parses val and assigns it directly into v for builtin +// convertible kinds, avoiding the reflect.Value boxing of the Converter API. +// handled reports whether the kind is builtin-convertible; ok reports whether +// val parsed successfully. v is only modified on success. +func setBuiltinKind(v reflect.Value, k reflect.Kind, val string) (handled, ok bool) { + switch k { + case boolType: + if val == "on" { + v.SetBool(true) + return true, true + } + b, err := strconv.ParseBool(val) + if err != nil { + return true, false + } + v.SetBool(b) + case stringType: + v.SetString(val) + case intType: + n, err := utils.ParseInt(val) + if err != nil || int64(int(n)) != n { + return true, false + } + v.SetInt(n) + case int8Type: + n, err := utils.ParseInt8(val) + if err != nil { + return true, false + } + v.SetInt(int64(n)) + case int16Type: + n, err := utils.ParseInt16(val) + if err != nil { + return true, false + } + v.SetInt(int64(n)) + case int32Type: + n, err := utils.ParseInt32(val) + if err != nil { + return true, false + } + v.SetInt(int64(n)) + case int64Type: + n, err := utils.ParseInt(val) + if err != nil { + return true, false } + v.SetInt(n) + case uintType: + n, err := utils.ParseUint(val) + if err != nil || uint64(uint(n)) != n { + return true, false + } + v.SetUint(n) + case uint8Type: + n, err := utils.ParseUint8(val) + if err != nil { + return true, false + } + v.SetUint(uint64(n)) + case uint16Type: + n, err := utils.ParseUint16(val) + if err != nil { + return true, false + } + v.SetUint(uint64(n)) + case uint32Type: + n, err := utils.ParseUint32(val) + if err != nil { + return true, false + } + v.SetUint(uint64(n)) + case uint64Type: + n, err := utils.ParseUint(val) + if err != nil { + return true, false + } + v.SetUint(n) + case float32Type: + f, err := utils.ParseFloat32(val) + if err != nil { + return true, false + } + v.SetFloat(float64(f)) + case float64Type: + f, err := utils.ParseFloat64(val) + if err != nil { + return true, false + } + v.SetFloat(f) + default: + return false, false } - return invalidValue + return true, true } diff --git a/src/vendor/github.com/gofiber/schema/decoder.go b/src/vendor/github.com/gofiber/schema/decoder.go index 08540878..0b8b6bcf 100644 --- a/src/vendor/github.com/gofiber/schema/decoder.go +++ b/src/vendor/github.com/gofiber/schema/decoder.go @@ -8,6 +8,7 @@ import ( "encoding" "errors" "fmt" + "maps" "mime/multipart" "reflect" "strings" @@ -18,6 +19,10 @@ const ( defaultMaxSize = 16000 ) +// errNotPointerToStruct is returned by Decode for invalid destinations; +// hoisted so the check does not allocate on every call. +var errNotPointerToStruct = errors.New("schema: interface must be a pointer to struct") + var decodeValueBufferPool = sync.Pool{ New: func() any { buf := make([]reflect.Value, 0, 8) @@ -92,24 +97,14 @@ func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) // // See the package documentation for a full explanation of the mechanics. func (d *Decoder) Decode(dst interface{}, src map[string][]string, files ...map[string][]*multipart.FileHeader) (err error) { - var multipartFiles map[string][]*multipart.FileHeader - - if len(files) > 0 { - multipartFiles = files[0] - } - - // Add files as empty string values to src in order to make path parsing work easily - for path := range multipartFiles { - src[path] = []string{""} - } - v := reflect.ValueOf(dst) if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { - return errors.New("schema: interface must be a pointer to struct") + return errNotPointerToStruct } // Catch panics from the decoder and return them as an error. - // This is needed because the decoder calls reflect and reflect panics + // This is needed because the decoder calls reflect and reflect panics. + // Installed before any other work so nothing can crash the caller. defer func() { if r := recover(); r != nil { if e, ok := r.(error); ok { @@ -120,19 +115,37 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string, files ...map[ } }() + var multipartFiles map[string][]*multipart.FileHeader + + if len(files) > 0 { + multipartFiles = files[0] + } + + // Add files as empty string values to the decode view so path parsing + // works uniformly. Work on a copy: the caller's src map must not be + // mutated (and a caller-provided value under a file's key must not be + // overwritten in it). + if len(multipartFiles) > 0 { + merged := make(map[string][]string, len(src)+len(multipartFiles)) + maps.Copy(merged, src) + for path := range multipartFiles { + merged[path] = []string{""} + } + src = merged + } + v = v.Elem() t := v.Type() + rootInfo := d.cache.get(t) var multiErrors MultiError for path, values := range src { - if parts, err := d.cache.parsePath(path, t); err == nil { - if filesSlice, ok := multipartFiles[path]; ok { - if err = d.decode(v, path, parts, values, filesSlice); err != nil { - multiErrors = appendError(multiErrors, path, err) - } - } else { - if err = d.decode(v, path, parts, values, nil); err != nil { - multiErrors = appendError(multiErrors, path, err) - } + if parts, err := d.cache.parsePathInfo(path, rootInfo); err == nil { + var filesSlice []*multipart.FileHeader + if multipartFiles != nil { + filesSlice = multipartFiles[path] + } + if err = d.decode(v, path, parts, values, filesSlice); err != nil { + multiErrors = appendError(multiErrors, path, err) } } else { if errors.Is(err, errIndexTooLarge) { @@ -142,8 +155,10 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string, files ...map[ } } } - multiErrors = mergeErrors(multiErrors, d.setDefaults(t, v, src, "")) - multiErrors = mergeErrors(multiErrors, d.checkRequired(t, src)) + if rootInfo.needsDefaultsWalk { + multiErrors = mergeErrors(multiErrors, d.setDefaults(t, v, src, "")) + } + multiErrors = mergeErrors(multiErrors, d.checkRequired(rootInfo, src)) if len(multiErrors) > 0 { return multiErrors } @@ -155,24 +170,29 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string, files ...map[ // nested structs can also have default tags func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value, src map[string][]string, prefix string) MultiError { struc := d.cache.get(t) - if struc == nil { - // unexpected, cache.get never return nil - return MultiError{"default-" + t.Name(): errors.New("cache fail")} + // Skip the walk entirely when it can have no effect (no default tags and + // no anonymous embedded pointers to allocate anywhere in the tree) — the + // overwhelmingly common case. + if !struc.needsDefaultsWalk { + return nil } - errs := MultiError{} + var errs MultiError - if v.Type().Kind() == reflect.Struct { - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - if field.Type().Kind() == reflect.Ptr && field.IsNil() && v.Type().Field(i).Anonymous { - field.Set(reflect.New(field.Type().Elem())) - } + // Allocate nil anonymous embedded pointer fields so their promoted + // fields stay reachable. + for _, idx := range struc.anonymousPtrFields { + if field := v.Field(idx); field.IsNil() { + field.Set(reflect.New(field.Type().Elem())) } } for _, f := range struc.fields { - vCurrent := v.FieldByName(f.name) + vCurrent := walkIndexChain(v, f.index) + if !vCurrent.IsValid() { + // Unreachable behind an unsettable nil embedded pointer. + continue + } if vCurrent.Type().Kind() == reflect.Struct && f.defaultValue == "" { errs = mergeErrors(errs, d.setDefaults(vCurrent.Type(), vCurrent, src, prefix+f.canonicalAlias+".")) @@ -186,8 +206,6 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value, src map[string][] if f.typ.Kind() == reflect.Struct { errs = appendError(errs, "default-"+f.name, errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")) } else if f.typ.Kind() == reflect.Slice { - vals := strings.Split(f.defaultValue, "|") - // check if slice has one of the supported types for defaults conv := getBuiltinConverter(f.typ.Elem().Kind()) if conv == nil { @@ -195,15 +213,19 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value, src map[string][] continue } - defaultSlice := reflect.MakeSlice(f.typ, 0, cap(vals)) - for _, val := range vals { + elemT := f.typ.Elem() + defaultSlice := reflect.MakeSlice(f.typ, 0, strings.Count(f.defaultValue, "|")+1) + for val := range strings.SplitSeq(f.defaultValue, "|") { // this check is to handle if the wrong value is provided convertedVal := conv(val) if !convertedVal.IsValid() { errs = appendError(errs, "default-"+f.name, fmt.Errorf("failed setting default: %s is not compatible with field %s type", val, f.name)) break } - defaultSlice = reflect.Append(defaultSlice, convertedVal) + // Builtin converters return the underlying kind; convert to + // the (possibly named) element type before appending, else + // reflect.Append panics for e.g. []MyInt. + defaultSlice = reflect.Append(defaultSlice, convertedVal.Convert(elemT)) } vCurrent.Set(defaultSlice) } else if f.typ.Kind() == reflect.Ptr { @@ -214,8 +236,19 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value, src map[string][] } // this check is to handle if the wrong value is provided - if convertedVal := convertPointer(t1.Kind(), f.defaultValue); convertedVal.IsValid() { - vCurrent.Set(convertedVal) + if conv := getBuiltinConverter(t1.Kind()); conv != nil { + if convertedVal := conv(f.defaultValue); convertedVal.IsValid() { + // Build a pointer of the field's actual element type: + // the converter yields the underlying kind, which is + // convertible to the (possibly named) element type, + // and *elem is assignable to the field even when the + // field's type is itself a named pointer type (e.g. + // type MyIntPtr *MyInt), where converting a *int + // directly would panic. + p := reflect.New(t1) + p.Elem().Set(convertedVal.Convert(t1)) + vCurrent.Set(p) + } } } else { // this check is to handle if the wrong value is provided @@ -223,7 +256,9 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value, src map[string][] if conv == nil { errs = appendError(errs, "default-"+f.name, errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")) } else if convertedVal := conv(f.defaultValue); convertedVal.IsValid() { - vCurrent.Set(convertedVal) + // Builtin converters return the underlying kind; convert to + // the field's (possibly named) type before assigning. + vCurrent.Set(convertedVal.Convert(f.typ)) } } } @@ -247,15 +282,14 @@ func fieldProvided(src map[string][]string, prefix string, f *fieldInfo) bool { // checkRequired checks whether required fields are empty // -// check type t recursively if t has struct fields. +// The set of required fields (including those of nested structs) is +// precomputed once per struct type in structInfo.requiredFields, so this +// only performs the per-request emptiness checks against src. // // src is the source map for decoding, we use it here to see if those required fields are included in src -func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string) MultiError { - m, errs := d.findRequiredFields(t, "", "") - if len(m) == 0 { - return errs - } - for key, fields := range m { +func (d *Decoder) checkRequired(info *structInfo, src map[string][]string) MultiError { + var errs MultiError + for key, fields := range info.requiredFields { if isEmptyFields(fields, src) { errs = appendError(errs, key, EmptyFieldError{Key: key}) } @@ -263,58 +297,41 @@ func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string) MultiEr return errs } -// findRequiredFields recursively searches the struct type t for required fields. -// -// canonicalPrefix and searchPrefix are used to resolve full paths in dotted notation -// for nested struct fields. canonicalPrefix is a complete path which never omits -// any embedded struct fields. searchPrefix is a user-friendly path which may omit -// some embedded struct fields to point promoted fields. -func (d *Decoder) findRequiredFields(t reflect.Type, canonicalPrefix, searchPrefix string) (map[string][]fieldWithPrefix, MultiError) { - struc := d.cache.get(t) - if struc == nil { - // unexpected, cache.get never return nil - return nil, MultiError{canonicalPrefix + "*": errors.New("cache fail")} - } - - m := map[string][]fieldWithPrefix{} - errs := MultiError{} - for _, f := range struc.fields { - if f.typ.Kind() == reflect.Struct { - fcprefix := canonicalPrefix + f.canonicalAlias + "." - for _, fspath := range f.paths(searchPrefix) { - fm, ferrs := d.findRequiredFields(f.typ, fcprefix, fspath+".") - for key, fields := range fm { - m[key] = append(m[key], fields...) - } - errs.merge(ferrs) - } - } - if f.isRequired { - key := canonicalPrefix + f.canonicalAlias - m[key] = append(m[key], fieldWithPrefix{ - fieldInfo: f, - prefix: searchPrefix, - }) - } - } - return m, errs -} - type fieldWithPrefix struct { *fieldInfo prefix string + // searchPaths lists the src keys this required field answers to, and + // searchPathDots the corresponding nested-key prefixes; both are + // precomputed at cache-build time so per-request checks allocate + // nothing. + searchPaths []string + searchPathDots []string +} + +func newFieldWithPrefix(f *fieldInfo, prefix string) fieldWithPrefix { + paths := f.paths(prefix) + dots := make([]string, len(paths)) + for i, p := range paths { + dots[i] = p + "." + } + return fieldWithPrefix{ + fieldInfo: f, + prefix: prefix, + searchPaths: paths, + searchPathDots: dots, + } } // isEmptyFields returns true if all of specified fields are empty. func isEmptyFields(fields []fieldWithPrefix, src map[string][]string) bool { for _, f := range fields { - for _, path := range f.paths(f.prefix) { + for i, path := range f.searchPaths { v, ok := src[path] if ok && !isEmpty(f.typ, v) { return false } // Check for nested keys that match this field. - pathDot := path + "." + pathDot := f.searchPathDots[i] for key, val := range src { if len(val) == 0 { continue @@ -337,7 +354,10 @@ func isEmpty(t reflect.Type, value []string) bool { return true } switch t.Kind() { - case boolType, float32Type, float64Type, intType, int8Type, int32Type, int64Type, stringType, uint8Type, uint16Type, uint32Type, uint64Type: + case boolType, float32Type, float64Type, + intType, int8Type, int16Type, int32Type, int64Type, + stringType, + uintType, uint8Type, uint16Type, uint32Type, uint64Type: return len(value[0]) == 0 } return false @@ -415,11 +435,38 @@ func isMultipartField(typ reflect.Type) bool { return false } +// walkIndexChain walks v along a struct field index chain. Chains longer +// than one element traverse embedded structs; intermediate nil pointers are +// allocated so promoted fields stay reachable. It returns the zero Value +// when the chain is blocked by a nil pointer that cannot be set (an +// unexported embedded pointer), which callers treat as an unreachable +// field. +func walkIndexChain(v reflect.Value, chain []int) reflect.Value { + for j, fi := range chain { + if j > 0 && v.Kind() == reflect.Ptr { + if v.IsNil() { + if !v.CanSet() { + return reflect.Value{} + } + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + v = v.Field(fi) + } + return v +} + // decode fills a struct field using a parsed path. func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string, files []*multipart.FileHeader) error { // Get the field walking the struct fields by index. - for _, name := range parts[0].path { - if v.Type().Kind() == reflect.Ptr { + for _, hop := range parts[0].hops { + // A previous hop may have been blocked by an unsettable nil + // embedded pointer; the field is unreachable then. + if !v.IsValid() { + return nil + } + if v.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } @@ -427,11 +474,13 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values } // Allocate embedded anonymous pointers required for promoted fields. - if v.Type().Kind() == reflect.Struct { - d.ensureAnonymousPtrs(v) + for _, idx := range hop.ensure { + if f := v.Field(idx); f.IsNil() { + f.Set(reflect.New(f.Type().Elem())) + } } - v = v.FieldByName(name) + v = walkIndexChain(v, hop.index) } // Don't even bother for unexported fields. @@ -440,7 +489,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values } // Check multipart files - if mp := handleMultipartField(v, files); mp { + if parts[0].field.isMultipart && handleMultipartField(v, files) { return nil } @@ -462,8 +511,11 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values return fmt.Errorf("%v index %d is larger than the configured maxSize %d", v.Kind(), idx, d.maxSize) } if v.IsNil() || v.Len() < idx+1 { + // Grow into a fresh backing array: extending within existing + // capacity would write into memory the caller may still share + // through other slices aliasing the original array. value := reflect.MakeSlice(t, idx+1, idx+1) - if v.Len() < idx+1 { + if v.Len() > 0 { // Resize it. reflect.Copy(value, v) } @@ -474,15 +526,15 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values // Get the converter early in case there is one for a slice type. conv := d.cache.converter(t) - m := isTextUnmarshaler(v) + // The encoding.TextUnmarshaler facts for v's type are precomputed per + // field; instances are bound to live values where needed below. For an + // elem part (path terminated at a slice index) v is an element of the + // slice field, so the element type's facts apply. + m := parts[0].field.derefUnmarshaler + if parts[0].elem { + m = parts[0].field.elemUnmarshaler + } if conv == nil && t.Kind() == reflect.Slice && m.IsSliceElement { - itemsBuf := decodeValueBufferPool.Get().(*[]reflect.Value) - items := (*itemsBuf)[:0] - defer func() { - clear(items) - *itemsBuf = items[:0] - decodeValueBufferPool.Put(itemsBuf) - }() elemT := t.Elem() isPtrElem := elemT.Kind() == reflect.Ptr if isPtrElem { @@ -490,7 +542,8 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values } // Try to get a converter for the element type. - conv := d.cache.converter(elemT) + customConv := d.cache.converter(elemT) + conv := customConv if conv == nil { conv = getBuiltinConverter(elemT.Kind()) if conv == nil { @@ -500,10 +553,25 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values } } + // Fast path: builtin element kinds without unmarshalers, custom + // converters or pointer elements decode straight into a fresh slice, + // avoiding one reflect.Value allocation per element. + if customConv == nil && !m.IsValid && !isPtrElem { + return d.decodeBuiltinSlice(v, t, path, values) + } + + itemsBuf := decodeValueBufferPool.Get().(*[]reflect.Value) + items := (*itemsBuf)[:0] + defer func() { + clear(items) + *itemsBuf = items[:0] + decodeValueBufferPool.Put(itemsBuf) + }() + for key, value := range values { if value == "" { if d.zeroEmpty { - items = append(items, reflect.Zero(elemT)) + items = append(items, reflect.Zero(t.Elem())) } } else if m.IsValid { u := reflect.New(elemT) @@ -521,39 +589,22 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values } if m.IsSliceElementPtr { items = append(items, u.Elem().Addr()) - } else if u.Kind() == reflect.Ptr { - items = append(items, u.Elem()) } else { - items = append(items, u) + // u is always a pointer from reflect.New; store the + // pointed-to value. + items = append(items, u.Elem()) } } else if item := conv(value); item.IsValid() { - if isPtrElem { - ptr := reflect.New(elemT) - ptr.Elem().Set(item) - item = ptr - } - if item.Type() != elemT && !isPtrElem { - item = item.Convert(elemT) - } - items = append(items, item) + items = appendConvertedItem(items, item, elemT, isPtrElem) } else { - if strings.Contains(value, ",") { - values := strings.Split(value, ",") - for _, value := range values { + if strings.IndexByte(value, ',') != -1 { + for value := range strings.SplitSeq(value, ",") { if value == "" { if d.zeroEmpty { - items = append(items, reflect.Zero(elemT)) + items = append(items, reflect.Zero(t.Elem())) } } else if item := conv(value); item.IsValid() { - if isPtrElem { - ptr := reflect.New(elemT) - ptr.Elem().Set(item) - item = ptr - } - if item.Type() != elemT && !isPtrElem { - item = item.Convert(elemT) - } - items = append(items, item) + items = appendConvertedItem(items, item, elemT, isPtrElem) } else { return ConversionError{ Key: path, @@ -571,7 +622,10 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values } } } - value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...) + value := reflect.MakeSlice(t, len(items), len(items)) + for i, item := range items { + value.Index(i).Set(item) + } v.Set(value) } else { val := "" @@ -605,8 +659,10 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values v.Set(reflect.Indirect(u)) } else { // If the value implements the encoding.TextUnmarshaler interface - // apply UnmarshalText as the converter - if err := m.Unmarshaler.UnmarshalText([]byte(val)); err != nil { + // apply UnmarshalText as the converter, binding it to the + // live value. + um, _ := reflect.TypeAssert[encoding.TextUnmarshaler](v) + if err := um.UnmarshalText([]byte(val)); err != nil { return ConversionError{ Key: path, Type: t, @@ -619,10 +675,8 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values if d.zeroEmpty { v.Set(reflect.Zero(t)) } - } else if conv := getBuiltinConverter(t.Kind()); conv != nil { - if value := conv(val); value.IsValid() { - v.Set(value.Convert(t)) - } else { + } else if handled, ok := setBuiltinKind(v, t.Kind(), val); handled { + if !ok { return ConversionError{ Key: path, Type: t, @@ -636,26 +690,102 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values return nil } -func (d *Decoder) ensureAnonymousPtrs(v reflect.Value) { - info := d.cache.get(v.Type()) - for _, idx := range info.anonymousPtrFields { - field := v.Field(idx) - if field.IsNil() { - field.Set(reflect.New(field.Type().Elem())) +// appendConvertedItem converts a builtin/custom converter result to the slice +// element type and appends it, wrapping it in a freshly allocated pointer for +// pointer-element slices. The conversion must happen before the pointer wrap: +// builtin converters return the underlying kind (e.g. int for a named +// `type MyInt int`), which is not assignable to the named element type, so +// Set-ing it into a *MyInt without converting first panics. +func appendConvertedItem(items []reflect.Value, item reflect.Value, elemT reflect.Type, isPtrElem bool) []reflect.Value { + if item.Type() != elemT { + item = item.Convert(elemT) + } + if isPtrElem { + ptr := reflect.New(elemT) + ptr.Elem().Set(item) + item = ptr + } + return append(items, item) +} + +// decodeBuiltinSlice decodes values into the slice field v of type t whose +// elements are builtin-convertible kinds, parsing directly into slice slots +// instead of boxing every element in a reflect.Value. The slice is built +// detached and only assigned to v when every value parsed, matching the +// all-or-nothing behavior of the generic path. +// +// A value that fails to parse as a whole is retried as a comma-separated +// list. For non-string kinds a value containing a comma can never parse as a +// whole (no builtin syntax admits commas — pinned by a test), and string +// values always parse, so item boundaries are knowable upfront: the slice is +// sized by a cheap comma count (an upper bound, since empty items may be +// skipped) and truncated to the filled length at the end. +func (d *Decoder) decodeBuiltinSlice(v reflect.Value, t reflect.Type, path string, values []string) error { + elemT := t.Elem() + k := elemT.Kind() + split := k != reflect.String + + n := 0 + for _, value := range values { + if split { + n += strings.Count(value, ",") + } + n++ + } + + sl := reflect.MakeSlice(t, n, n) + i := 0 + for key, value := range values { + switch { + case value == "": + if d.zeroEmpty { + i++ // slot stays zero + } + case split && strings.IndexByte(value, ',') != -1: + for item := range strings.SplitSeq(value, ",") { + if item == "" { + if d.zeroEmpty { + i++ // slot stays zero + } + continue + } + if _, ok := setBuiltinKind(sl.Index(i), k, item); !ok { + return ConversionError{ + Key: path, + Type: elemT, + Index: key, + } + } + i++ + } + default: + if _, ok := setBuiltinKind(sl.Index(i), k, value); !ok { + return ConversionError{ + Key: path, + Type: elemT, + Index: key, + } + } + i++ } } + if i < n { + sl = sl.Slice(0, i) + } + v.Set(sl) + return nil } func isTextUnmarshaler(v reflect.Value) unmarshaler { // Create a new unmarshaller instance m := unmarshaler{} - if m.Unmarshaler, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](v); m.IsValid { + if _, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](v); m.IsValid { return m } // As the UnmarshalText function should be applied to the pointer of the // type, we check that type to see if it implements the necessary // method. - if m.Unmarshaler, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](reflect.New(v.Type())); m.IsValid { + if _, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](reflect.New(v.Type())); m.IsValid { m.IsPtr = true return m } @@ -666,31 +796,27 @@ func isTextUnmarshaler(v reflect.Value) unmarshaler { t = t.Elem() } if t.Kind() == reflect.Slice { - // Check if the slice implements encoding.TextUnmarshaller - if m.Unmarshaler, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](v); m.IsValid { - return m - } - // If t is a pointer slice, check if its elements implement - // encoding.TextUnmarshaler + // The slice type itself cannot implement encoding.TextUnmarshaler + // here: the value-level assert above already covered it. Check + // whether the elements do. m.IsSliceElement = true if t = t.Elem(); t.Kind() == reflect.Ptr { t = reflect.PointerTo(t.Elem()) - v = reflect.Zero(t) m.IsSliceElementPtr = true - m.Unmarshaler, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](v) + _, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](reflect.Zero(t)) return m } } - v = reflect.New(t) - m.Unmarshaler, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](v) + _, m.IsValid = reflect.TypeAssert[encoding.TextUnmarshaler](reflect.New(t)) return m } // TextUnmarshaler helpers ---------------------------------------------------- -// unmarshaller contains information about a TextUnmarshaler type +// unmarshaler describes how a type relates to encoding.TextUnmarshaler. +// It carries type-level facts only; the decoder binds instances to live +// values at the point of use. type unmarshaler struct { - Unmarshaler encoding.TextUnmarshaler // IsValid indicates whether the resolved type indicated by the other // flags implements the encoding.TextUnmarshaler interface. IsValid bool @@ -772,14 +898,6 @@ func (e MultiError) Error() string { return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1) } -func (e MultiError) merge(errors MultiError) { - for key, err := range errors { - if e[key] == nil { - e[key] = err - } - } -} - func appendRequiredField(m map[string][]fieldWithPrefix, key string, field fieldWithPrefix) map[string][]fieldWithPrefix { if m == nil { m = make(map[string][]fieldWithPrefix) diff --git a/src/vendor/github.com/gofiber/schema/encoder.go b/src/vendor/github.com/gofiber/schema/encoder.go index d8f0a1e8..b1d53b36 100644 --- a/src/vendor/github.com/gofiber/schema/encoder.go +++ b/src/vendor/github.com/gofiber/schema/encoder.go @@ -5,16 +5,66 @@ import ( "fmt" "reflect" "strconv" + "sync" + "sync/atomic" utils "github.com/gofiber/utils/v2" ) type encoderFunc func(reflect.Value) string +// errNotStruct is returned by Encode for invalid sources; hoisted so the +// check does not allocate on every call. +var errNotStruct = errors.New("schema: interface must be a struct") + +// errNilDst is returned by Encode when the destination map is nil, which +// would otherwise panic on the first map assignment. +var errNilDst = errors.New("schema: dst map must not be nil") + // Encoder encodes values from a struct into url.Values. type Encoder struct { cache *cache regenc map[reflect.Type]encoderFunc + // encCache memoizes the per-struct-type encoding plan + // (map[reflect.Type][]encField) so tags are parsed and encoder + // functions resolved once per type instead of on every Encode call. + encCache sync.Map + // encGen is bumped before encCache is cleared on configuration changes; + // structInfo snapshots it before building a plan and refuses to store + // the plan if it changed, so a build racing a reconfiguration cannot + // re-insert a stale plan after the clear. + encGen atomic.Uint64 +} + +// encPlan tags a per-type encoding plan with the configuration generation it +// was built under: hit-validation ignores plans from older generations, so +// any Encode starting after a reconfiguration returns observes the new +// configuration even if a racing build stored a stale plan after the clear. +type encPlan struct { + fields []encField + gen uint64 +} + +// encField is the precomputed encoding plan for one struct field. +type encField struct { + name string + enc encoderFunc // immediate encoder; nil for structs and slices + elemEnc encoderFunc // slice element encoder, when the field is a slice + idx int + omitEmpty bool + // recurseStructPtr marks pointer-to-struct fields without a custom + // encoder: non-nil values are encoded by recursing into the element. + recurseStructPtr bool + isStruct bool + // nilAsNull marks pointer fields whose element has no immediate + // encoder (structs recursed via recurseStructPtr, or unsupported + // types): nil values encode as "null", matching the closure behavior + // pointer fields with encodable elements get. + nilAsNull bool + // elemPtrNil marks slice fields whose element is a pointer type with no + // encoder (e.g. []*Struct): nil elements encode as "null" (as they did + // historically), while a non-nil such element is an error. + elemPtrNil bool } // NewEncoder returns a new Encoder with defaults. @@ -25,7 +75,23 @@ func NewEncoder() *Encoder { // Encode encodes a struct into map[string][]string. // // Intended for use with url.Values. -func (e *Encoder) Encode(src interface{}, dst map[string][]string) error { +func (e *Encoder) Encode(src interface{}, dst map[string][]string) (err error) { + if dst == nil { + return errNilDst + } + + // Catch panics from reflection or user-registered encoders and return + // them as an error instead of crashing the caller, mirroring Decode. + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + err = e + } else { + err = fmt.Errorf("schema: panic while encoding: %v", r) + } + } + }() + v := reflect.ValueOf(src) return e.encode(v, dst) @@ -33,18 +99,79 @@ func (e *Encoder) Encode(src interface{}, dst map[string][]string) error { // RegisterEncoder registers a converter for encoding a custom type. func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) { + e.cache.l.Lock() e.regenc[reflect.TypeOf(value)] = encoder + e.cache.l.Unlock() + e.encGen.Add(1) + e.encCache.Clear() } // SetAliasTag changes the tag used to locate custom field aliases. // The default tag is "schema". func (e *Encoder) SetAliasTag(tag string) { + e.cache.l.Lock() e.cache.tag = tag + e.cache.l.Unlock() + e.encGen.Add(1) + e.encCache.Clear() } -// isValidStructPointer test if input value is a valid struct pointer. -func isValidStructPointer(v reflect.Value) bool { - return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct +// structInfo returns the cached encoding plan for struct type t, building it +// on first use. The build reads the tag and registered encoders under the +// configuration lock; the generation re-checks around the cache store keep a +// build racing a reconfiguration from inserting a stale plan. +func (e *Encoder) structInfo(t reflect.Type) []encField { + gen := e.encGen.Load() + if cached, ok := e.encCache.Load(t); ok { + // Ignore plans built under an older configuration; fall through and + // rebuild (the fresh plan overwrites the stale entry). + if p := cached.(*encPlan); p.gen == gen { + return p.fields + } + } + e.cache.l.RLock() + tag := e.cache.tag + fields := make([]encField, 0, t.NumField()) + for i := 0; i < t.NumField(); i++ { + sf := t.Field(i) + name, opts := fieldAlias(sf, tag) + if name == "-" { + continue + } + ft := sf.Type + f := encField{ + idx: i, + name: name, + omitEmpty: opts.Contains("omitempty"), + recurseStructPtr: ft.Kind() == reflect.Ptr && + ft.Elem().Kind() == reflect.Struct && + !e.hasCustomEncoder(ft), + enc: typeEncoder(ft, e.regenc), + } + if f.enc == nil { + switch ft.Kind() { + case reflect.Struct: + f.isStruct = true + case reflect.Slice: + f.elemEnc = typeEncoder(ft.Elem(), e.regenc) + if f.elemEnc == nil && ft.Elem().Kind() == reflect.Ptr { + f.elemPtrNil = true + } + case reflect.Ptr: + f.nilAsNull = true + } + } + fields = append(fields, f) + } + e.cache.l.RUnlock() + // Don't cache a plan whose inputs (tag, registered encoders) changed + // while it was being built; the next call rebuilds it fresh. Even if a + // stale plan slips in after the clear, its generation tag keeps it from + // ever being served. + if e.encGen.Load() == gen { + e.encCache.Store(t, &encPlan{fields: fields, gen: gen}) + } + return fields } func isZero(v reflect.Value) bool { @@ -81,75 +208,105 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { v = v.Elem() } if v.Kind() != reflect.Struct { - return errors.New("schema: interface must be a struct") + return errNotStruct } - t := v.Type() - errors := MultiError{} + var errs MultiError - for i := 0; i < v.NumField(); i++ { - fieldValue := v.Field(i) - fieldType := fieldValue.Type() - name, opts := fieldAlias(t.Field(i), e.cache.tag) - if name == "-" { - continue - } + fields := e.structInfo(v.Type()) + for i := range fields { + f := &fields[i] + fieldValue := v.Field(f.idx) // Encode struct pointer types if the field is a valid pointer and a struct. - if isValidStructPointer(fieldValue) && !e.hasCustomEncoder(fieldType) { - err := e.encode(fieldValue.Elem(), dst) - if err != nil { - errors[fieldValue.Elem().Type().String()] = err + if f.recurseStructPtr && !fieldValue.IsNil() { + if err := e.encode(fieldValue.Elem(), dst); err != nil { + errs = setError(errs, fieldValue.Elem().Type().String(), err) } continue } - encFunc := typeEncoder(fieldType, e.regenc) - // Encode non-slice types and custom implementations immediately. - if encFunc != nil { - if opts.Contains("omitempty") && isZero(fieldValue) { + if f.enc != nil { + if f.omitEmpty && isZero(fieldValue) { continue } - value := encFunc(fieldValue) - dst[name] = append(dst[name], value) + dst[f.name] = append(dst[f.name], f.enc(fieldValue)) continue } - if fieldType.Kind() == reflect.Struct { - err := e.encode(fieldValue, dst) - if err != nil { - errors[fieldType.String()] = err + if f.nilAsNull && fieldValue.IsNil() { + if f.omitEmpty { + continue } + dst[f.name] = append(dst[f.name], "null") continue } - if fieldType.Kind() == reflect.Slice { - encFunc = typeEncoder(fieldType.Elem(), e.regenc) + if f.isStruct { + if err := e.encode(fieldValue, dst); err != nil { + errs = setError(errs, fieldValue.Type().String(), err) + } + continue } - if encFunc == nil { - errors[fieldType.String()] = fmt.Errorf("schema: encoder not found for %v", fieldValue) + // A non-slice field with no encoder (map, chan, array, or a non-nil + // pointer to an unencodable type), or a slice whose element type is + // itself unencodable and not a pointer (e.g. []Struct), cannot be + // encoded — historically this errored unconditionally. + if fieldValue.Kind() != reflect.Slice || (f.elemEnc == nil && !f.elemPtrNil) { + errs = setError(errs, fieldValue.Type().String(), fmt.Errorf("schema: encoder not found for %v", fieldValue)) continue } - // Encode a slice. - if fieldValue.Len() == 0 && opts.Contains("omitempty") { + // Encode a slice. An empty slice has nothing to encode, so it is + // skipped under omitempty (and otherwise emitted empty). + n := fieldValue.Len() + if n == 0 && f.omitEmpty { continue } - dst[name] = []string{} - for j := 0; j < fieldValue.Len(); j++ { - dst[name] = append(dst[name], encFunc(fieldValue.Index(j))) + values := make([]string, n) + if f.elemEnc == nil { + // Pointer elements with no encoder (elemPtrNil): nil encodes as + // "null" (as historically), a non-nil such element is an error. + bad := false + for j := 0; j < n; j++ { + if fieldValue.Index(j).IsNil() { + values[j] = "null" + continue + } + errs = setError(errs, fieldValue.Type().String(), fmt.Errorf("schema: encoder not found for %v", fieldValue)) + bad = true + break + } + if bad { + continue + } + } else { + for j := 0; j < n; j++ { + values[j] = f.elemEnc(fieldValue.Index(j)) + } } + dst[f.name] = values } - if len(errors) > 0 { - return errors + if len(errs) > 0 { + return errs } return nil } +// setError lazily allocates m and stores err under key, overwriting any +// previous entry (matching the historical encoder error semantics). +func setError(m MultiError, key string, err error) MultiError { + if m == nil { + m = make(MultiError) + } + m[key] = err + return m +} + func (e *Encoder) hasCustomEncoder(t reflect.Type) bool { _, exists := e.regenc[t] return exists @@ -173,6 +330,12 @@ func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc { return encodeFloat64 case reflect.Ptr: f := typeEncoder(t.Elem(), reg) + if f == nil { + // No encoder for the element: report unsupported instead of + // returning a closure that would panic on non-nil values. + // Nil handling for such fields is done by encField.nilAsNull. + return nil + } return func(v reflect.Value) string { if v.IsNil() { return "null" diff --git a/src/vendor/github.com/gofiber/utils/v2/AGENTS.md b/src/vendor/github.com/gofiber/utils/v2/AGENTS.md new file mode 100644 index 00000000..dce12257 --- /dev/null +++ b/src/vendor/github.com/gofiber/utils/v2/AGENTS.md @@ -0,0 +1,20 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. + +## What this repository is + +gofiber/utils is a helper library for [Fiber](https://github.com/gofiber/fiber) and its middleware ecosystem. It exports fast, low-allocation helpers; the consumers live in other modules (fiber itself, middleware, user code). + +## Reviewing and proposing API changes + +- Exported functions without in-module callers are by design. Do not argue that a new public function or package should move to `internal/` because grep finds no non-test usage inside this module. In-module usage counts say nothing about the value of the API; exporting helpers for downstream modules is the purpose of this repository. +- Judge new exported API instead by: contract clarity (documented preconditions, aliasing rules, edge cases), test coverage (including property or fuzz tests for bit-twiddling code), naming consistency with the existing surface, and benchmark evidence for performance claims. +- Additive API is not a SemVer break, but it is a long-term maintenance commitment. Call out sharp-edged contracts (unchecked preconditions, approximate results, unsafe memory semantics) explicitly so maintainers approve them consciously. + +## Conventions + +- Performance claims are verified with benchstat (base vs. head on the same machine, at least `-count=10`), never with single runs. +- `make test` runs the suite with race detector and shuffle; `make lint` runs golangci-lint; `make format` applies gofumpt; `make benchfmt` aligns the benchmark tables in README.md. +- README.md is a function catalog with benchmark blocks. New exported functions need a README section, and benchmark numbers for touched paths should be regenerated in the same run. +- Code, comments, commit messages, and PR text are always written in English. diff --git a/src/vendor/github.com/gofiber/utils/v2/README.md b/src/vendor/github.com/gofiber/utils/v2/README.md index 46242a0f..3e8afccb 100644 --- a/src/vendor/github.com/gofiber/utils/v2/README.md +++ b/src/vendor/github.com/gofiber/utils/v2/README.md @@ -4,12 +4,15 @@ ![Test](https://github.com/gofiber/utils/workflows/Test/badge.svg) ![Codecov](https://img.shields.io/codecov/c/github/gofiber/utils?token=3Cr92CwaPQ&style=flat-square&logo=codecov&label=codecov) ![Linter](https://github.com/gofiber/utils/actions/workflows/lint.yml/badge.svg) +[![Benchmarks](https://img.shields.io/badge/%F0%9F%93%8A%20benchmarks-charts-00ACD7.svg)](https://gofiber.github.io/utils/benchmarks/) [![Discord](https://img.shields.io/badge/discord-join%20channel-7289DA)](https://gofiber.io/discord) A collection of common functions for [Fiber](https://github.com/gofiber/fiber) with better performance, fewer allocations, and fewer dependencies. ## Benchmarks +Per-commit benchmark charts: + Environment: goos: darwin goarch: arm64 @@ -18,351 +21,477 @@ cpu: Apple M2 Pro ```text // go test ./... -benchmem -run=^$ -bench=Benchmark_ -count=1 +// (swar's Benchmark_Load8_Fusion, an advisory codegen guard, is deliberately omitted) # Case Conversion -Benchmark_ToLowerBytes/empty/fiber-12 593913379 1.975 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/empty/fiber/unsafe-12 657748088 1.845 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/empty/default-12 367040010 3.281 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/http-get/fiber-12 365235189 3.393 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/http-get/fiber/unsafe-12 239092582 5.144 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/http-get/default-12 63143354 18.46 ns/op 8 B/op 1 allocs/op -Benchmark_ToLowerBytes/http-get-upper/fiber-12 94304095 12.86 ns/op 3 B/op 1 allocs/op -Benchmark_ToLowerBytes/http-get-upper/fiber/unsafe-12 239366296 5.057 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/http-get-upper/default-12 86047208 14.00 ns/op 8 B/op 1 allocs/op -Benchmark_ToLowerBytes/header-content-type-mixed/fiber-12 45169880 26.27 ns/op 48 B/op 1 allocs/op -Benchmark_ToLowerBytes/header-content-type-mixed/fiber/unsafe-12 95687102 12.70 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/header-content-type-mixed/default-12 25045154 47.56 ns/op 48 B/op 1 allocs/op -Benchmark_ToLowerBytes/large-lower/fiber-12 43856107 28.39 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/large-lower/fiber/unsafe-12 62907780 19.56 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/large-lower/default-12 17712895 69.18 ns/op 64 B/op 1 allocs/op -Benchmark_ToLowerBytes/large-upper/fiber-12 34286121 34.08 ns/op 64 B/op 1 allocs/op -Benchmark_ToLowerBytes/large-upper/fiber/unsafe-12 61959598 19.37 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/large-upper/default-12 16091281 75.42 ns/op 64 B/op 1 allocs/op -Benchmark_ToLowerBytes/large-mixed/fiber-12 33481714 35.06 ns/op 64 B/op 1 allocs/op -Benchmark_ToLowerBytes/large-mixed/fiber/unsafe-12 62478574 19.36 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/large-mixed/default-12 16091731 75.18 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpperBytes/empty/fiber-12 620292880 1.946 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/empty/fiber/unsafe-12 642997940 1.840 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/empty/default-12 366694138 3.324 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/http-get/fiber-12 95312475 12.75 ns/op 3 B/op 1 allocs/op -Benchmark_ToUpperBytes/http-get/fiber/unsafe-12 236603360 5.055 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/http-get/default-12 86667109 14.19 ns/op 8 B/op 1 allocs/op -Benchmark_ToUpperBytes/http-get-upper/fiber-12 360987979 3.425 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/http-get-upper/fiber/unsafe-12 236142552 5.096 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/http-get-upper/default-12 61760695 19.24 ns/op 8 B/op 1 allocs/op -Benchmark_ToUpperBytes/header-content-type-mixed/fiber-12 44394090 27.55 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpperBytes/header-content-type-mixed/fiber/unsafe-12 94647172 12.75 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/header-content-type-mixed/default-12 25090692 47.83 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpperBytes/large-lower/fiber-12 35558892 34.73 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpperBytes/large-lower/fiber/unsafe-12 62620129 19.31 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/large-lower/default-12 15487833 76.16 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpperBytes/large-upper/fiber-12 43901836 27.54 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/large-upper/fiber/unsafe-12 62027790 20.57 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/large-upper/default-12 14579604 82.82 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpperBytes/large-mixed/fiber-12 33719427 35.99 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpperBytes/large-mixed/fiber/unsafe-12 62008428 21.01 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/large-mixed/default-12 15469116 77.21 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpper/empty/fiber-12 613156160 1.955 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/empty/fiber/unsafe-12 649026514 1.838 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/empty/default-12 650956171 1.831 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/http-get/fiber-12 92095164 12.77 ns/op 3 B/op 1 allocs/op -Benchmark_ToUpper/http-get/fiber/unsafe-12 235750344 5.036 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/http-get/default-12 60473330 20.50 ns/op 8 B/op 1 allocs/op -Benchmark_ToUpper/http-get-upper/fiber-12 398272657 3.048 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/http-get-upper/fiber/unsafe-12 220124466 5.078 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/http-get-upper/default-12 233645295 5.051 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/header-content-type-mixed/fiber-12 36106831 33.14 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/header-content-type-mixed/fiber/unsafe-12 93201168 12.83 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/header-content-type-mixed/default-12 11069166 107.7 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/large-lower/fiber-12 29007373 41.16 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpper/large-lower/fiber/unsafe-12 61283246 19.47 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/large-lower/default-12 6855549 174.0 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpper/large-upper/fiber-12 43965034 28.00 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/large-upper/fiber/unsafe-12 53715409 20.68 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/large-upper/default-12 18805570 65.60 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/large-mixed/fiber-12 27005611 43.54 ns/op 64 B/op 1 allocs/op -Benchmark_ToUpper/large-mixed/fiber/unsafe-12 60924529 20.57 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/large-mixed/default-12 5662490 212.0 ns/op 64 B/op 1 allocs/op -Benchmark_ToLower/empty/fiber-12 607541103 1.972 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/empty/fiber/unsafe-12 631531857 1.969 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/empty/default-12 638706405 1.872 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/http-get/fiber-12 390062401 3.080 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/http-get/fiber/unsafe-12 231204609 5.190 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/http-get/default-12 278482443 4.408 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/http-get-upper/fiber-12 91051068 13.16 ns/op 3 B/op 1 allocs/op -Benchmark_ToLower/http-get-upper/fiber/unsafe-12 233437356 5.168 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/http-get-upper/default-12 59555317 22.03 ns/op 8 B/op 1 allocs/op -Benchmark_ToLower/header-content-type-mixed/fiber-12 32247734 35.17 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/header-content-type-mixed/fiber/unsafe-12 92616117 12.98 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/header-content-type-mixed/default-12 16339488 74.21 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/large-lower/fiber-12 37544337 33.77 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/large-lower/fiber/unsafe-12 61856332 19.83 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/large-lower/default-12 23513985 50.17 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/large-upper/fiber-12 28177750 44.45 ns/op 64 B/op 1 allocs/op -Benchmark_ToLower/large-upper/fiber/unsafe-12 61319127 20.23 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/large-upper/default-12 6700268 180.1 ns/op 64 B/op 1 allocs/op -Benchmark_ToLower/large-mixed/fiber-12 26804186 45.68 ns/op 64 B/op 1 allocs/op -Benchmark_ToLower/large-mixed/fiber/unsafe-12 61659141 19.97 ns/op 0 B/op 0 allocs/op -Benchmark_ToLower/large-mixed/default-12 5614172 213.1 ns/op 64 B/op 1 allocs/op +Benchmark_ToLowerBytes/empty/fiber-12 599341221 1.970 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/empty/fiber/unsafe-12 613673802 1.951 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/empty/default-12 363300402 3.316 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/http-get/fiber-12 350302147 3.434 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/http-get/fiber/unsafe-12 250488015 4.803 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/http-get/default-12 65883986 18.03 ns/op 8 B/op 1 allocs/op +Benchmark_ToLowerBytes/http-get-upper/fiber-12 95234625 12.53 ns/op 3 B/op 1 allocs/op +Benchmark_ToLowerBytes/http-get-upper/fiber/unsafe-12 242418936 4.802 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/http-get-upper/default-12 86405012 13.85 ns/op 8 B/op 1 allocs/op +Benchmark_ToLowerBytes/header-content-type-mixed/fiber-12 52263289 22.74 ns/op 48 B/op 1 allocs/op +Benchmark_ToLowerBytes/header-content-type-mixed/fiber/unsafe-12 131711175 9.101 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/header-content-type-mixed/default-12 25312339 47.13 ns/op 48 B/op 1 allocs/op +Benchmark_ToLowerBytes/large-lower/fiber-12 120148472 10.02 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/large-lower/fiber/unsafe-12 100000000 10.57 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/large-lower/default-12 16746064 71.43 ns/op 64 B/op 1 allocs/op +Benchmark_ToLowerBytes/large-upper/fiber-12 46270634 26.82 ns/op 64 B/op 1 allocs/op +Benchmark_ToLowerBytes/large-upper/fiber/unsafe-12 100000000 10.71 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/large-upper/default-12 15734505 76.92 ns/op 64 B/op 1 allocs/op +Benchmark_ToLowerBytes/large-mixed/fiber-12 46607823 26.06 ns/op 64 B/op 1 allocs/op +Benchmark_ToLowerBytes/large-mixed/fiber/unsafe-12 100000000 10.86 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/large-mixed/default-12 16291084 73.76 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpperBytes/empty/fiber-12 607678140 1.976 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/empty/fiber/unsafe-12 611928006 1.981 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/empty/default-12 362632894 3.309 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/http-get/fiber-12 95358867 12.55 ns/op 3 B/op 1 allocs/op +Benchmark_ToUpperBytes/http-get/fiber/unsafe-12 249490471 4.905 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/http-get/default-12 79248020 14.63 ns/op 8 B/op 1 allocs/op +Benchmark_ToUpperBytes/http-get-upper/fiber-12 365516280 3.330 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/http-get-upper/fiber/unsafe-12 249981619 4.803 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/http-get-upper/default-12 64343162 19.01 ns/op 8 B/op 1 allocs/op +Benchmark_ToUpperBytes/header-content-type-mixed/fiber-12 52506741 22.69 ns/op 48 B/op 1 allocs/op +Benchmark_ToUpperBytes/header-content-type-mixed/fiber/unsafe-12 130478352 9.350 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/header-content-type-mixed/default-12 22415607 54.44 ns/op 48 B/op 1 allocs/op +Benchmark_ToUpperBytes/large-lower/fiber-12 44765679 26.92 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpperBytes/large-lower/fiber/unsafe-12 100000000 13.68 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/large-lower/default-12 14464340 110.7 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpperBytes/large-upper/fiber-12 100000000 11.98 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/large-upper/fiber/unsafe-12 83808488 13.94 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/large-upper/default-12 13142096 87.35 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpperBytes/large-mixed/fiber-12 33038661 32.75 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpperBytes/large-mixed/fiber/unsafe-12 100000000 10.93 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/large-mixed/default-12 15034422 98.27 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpper/empty/fiber-12 595298628 2.309 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/empty/fiber/unsafe-12 595273404 2.021 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/empty/default-12 636980288 1.883 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/http-get/fiber-12 97025577 12.40 ns/op 3 B/op 1 allocs/op +Benchmark_ToUpper/http-get/fiber/unsafe-12 244711635 4.857 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/http-get/default-12 59790234 19.54 ns/op 8 B/op 1 allocs/op +Benchmark_ToUpper/http-get-upper/fiber-12 383218539 3.056 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/http-get-upper/fiber/unsafe-12 247851331 4.864 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/http-get-upper/default-12 223044249 5.182 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/header-content-type-mixed/fiber-12 52628502 24.13 ns/op 48 B/op 1 allocs/op +Benchmark_ToUpper/header-content-type-mixed/fiber/unsafe-12 127726341 9.233 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/header-content-type-mixed/default-12 11549816 104.0 ns/op 48 B/op 1 allocs/op +Benchmark_ToUpper/large-lower/fiber-12 42409070 26.04 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpper/large-lower/fiber/unsafe-12 100000000 11.00 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/large-lower/default-12 7051911 169.0 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpper/large-upper/fiber-12 123837582 9.590 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/large-upper/fiber/unsafe-12 100000000 10.83 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/large-upper/default-12 19115356 61.51 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/large-mixed/fiber-12 45872143 27.77 ns/op 64 B/op 1 allocs/op +Benchmark_ToUpper/large-mixed/fiber/unsafe-12 100000000 11.00 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/large-mixed/default-12 5927710 203.1 ns/op 64 B/op 1 allocs/op +Benchmark_ToLower/empty/fiber-12 613625554 1.952 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/empty/fiber/unsafe-12 572670608 1.989 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/empty/default-12 644769141 1.863 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/http-get/fiber-12 395302272 3.034 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/http-get/fiber/unsafe-12 252231990 4.813 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/http-get/default-12 284202345 4.219 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/http-get-upper/fiber-12 100000000 12.08 ns/op 3 B/op 1 allocs/op +Benchmark_ToLower/http-get-upper/fiber/unsafe-12 252859507 4.816 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/http-get-upper/default-12 60415860 19.54 ns/op 8 B/op 1 allocs/op +Benchmark_ToLower/header-content-type-mixed/fiber-12 53446660 22.49 ns/op 48 B/op 1 allocs/op +Benchmark_ToLower/header-content-type-mixed/fiber/unsafe-12 131152108 9.153 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/header-content-type-mixed/default-12 18039788 66.79 ns/op 48 B/op 1 allocs/op +Benchmark_ToLower/large-lower/fiber-12 120227584 9.982 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/large-lower/fiber/unsafe-12 100000000 10.68 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/large-lower/default-12 24070928 48.79 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/large-upper/fiber-12 44699118 25.92 ns/op 64 B/op 1 allocs/op +Benchmark_ToLower/large-upper/fiber/unsafe-12 100000000 10.74 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/large-upper/default-12 7075200 170.0 ns/op 64 B/op 1 allocs/op +Benchmark_ToLower/large-mixed/fiber-12 46426453 26.01 ns/op 64 B/op 1 allocs/op +Benchmark_ToLower/large-mixed/fiber/unsafe-12 100000000 10.74 ns/op 0 B/op 0 allocs/op +Benchmark_ToLower/large-mixed/default-12 5682778 204.3 ns/op 64 B/op 1 allocs/op # Add Trailing Slash -Benchmark_AddTrailingSlashBytes/empty-12 1000000000 0.6118 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashBytes/slash-only-12 1000000000 0.9030 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashBytes/short-no-slash-12 100000000 12.17 ns/op 4 B/op 1 allocs/op -Benchmark_AddTrailingSlashBytes/short-with-slash-12 1000000000 0.9096 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashBytes/path-no-slash-12 79386301 13.31 ns/op 16 B/op 1 allocs/op -Benchmark_AddTrailingSlashBytes/path-with-slash-12 1000000000 0.9079 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashString/empty-12 1000000000 0.3001 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashString/slash-only-12 1000000000 0.5217 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashString/short-no-slash-12 100000000 13.37 ns/op 4 B/op 1 allocs/op -Benchmark_AddTrailingSlashString/short-with-slash-12 1000000000 0.4548 ns/op 0 B/op 0 allocs/op -Benchmark_AddTrailingSlashString/path-no-slash-12 84763720 16.71 ns/op 16 B/op 1 allocs/op -Benchmark_AddTrailingSlashString/path-with-slash-12 1000000000 0.4536 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashBytes/empty-12 1000000000 0.5857 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashBytes/slash-only-12 1000000000 0.8694 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashBytes/short-no-slash-12 100000000 11.65 ns/op 4 B/op 1 allocs/op +Benchmark_AddTrailingSlashBytes/short-with-slash-12 1000000000 0.8867 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashBytes/path-no-slash-12 93543546 13.36 ns/op 16 B/op 1 allocs/op +Benchmark_AddTrailingSlashBytes/path-with-slash-12 1000000000 0.9011 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashString/empty-12 1000000000 0.2926 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashString/slash-only-12 1000000000 0.4482 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashString/short-no-slash-12 100000000 11.37 ns/op 4 B/op 1 allocs/op +Benchmark_AddTrailingSlashString/short-with-slash-12 1000000000 0.4502 ns/op 0 B/op 0 allocs/op +Benchmark_AddTrailingSlashString/path-no-slash-12 82427726 14.41 ns/op 16 B/op 1 allocs/op +Benchmark_AddTrailingSlashString/path-with-slash-12 1000000000 0.4529 ns/op 0 B/op 0 allocs/op # EqualFold -Benchmark_EqualFoldBytes/fiber-12 34227160 35.89 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFoldBytes/default-12 17905046 68.31 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFold/fiber-12 34215410 36.76 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFold/default-12 17103853 69.62 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFoldBytes/fiber-12 67368261 18.48 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFoldBytes/default-12 17774803 65.91 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFold/fiber-12 80501566 14.75 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFold/default-12 18314365 65.96 ns/op 0 B/op 0 allocs/op + +# Search +Benchmark_IndexAny2/7B/swar-12 282163779 4.297 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/7B/scalar-12 314553402 3.826 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/7B/stdlib-indexany-12 50085909 23.97 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/8B/swar-12 522780714 2.289 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/8B/scalar-12 271494799 4.206 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/8B/stdlib-indexany-12 44831599 26.78 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/16B/swar-12 393245086 3.047 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/16B/scalar-12 168476984 7.117 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/16B/stdlib-indexany-12 100000000 10.03 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/32B/swar-12 252199587 4.802 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/32B/scalar-12 84105762 14.27 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/32B/stdlib-indexany-12 70291048 17.37 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/64B/swar-12 144335962 8.290 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/64B/scalar-12 42694686 28.60 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/64B/stdlib-indexany-12 37615540 32.23 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/512B/swar-12 20231538 59.71 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/512B/scalar-12 5071586 233.6 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny2/512B/stdlib-indexany-12 4448119 274.2 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/7B/swar-12 221889592 5.397 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/7B/scalar-12 240859849 4.977 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/8B/swar-12 445742876 2.689 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/8B/scalar-12 211981612 5.620 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/16B/swar-12 326475568 3.739 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/16B/scalar-12 100000000 10.31 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/32B/swar-12 196365920 6.112 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/32B/scalar-12 60021255 19.81 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/64B/swar-12 100000000 10.98 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/64B/scalar-12 31180405 38.97 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/512B/swar-12 15466316 79.22 ns/op 0 B/op 0 allocs/op +Benchmark_IndexAny3/512B/scalar-12 3911382 307.1 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-8B/swar-12 332497509 3.634 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-8B/scalar-12 491493494 2.408 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-32B/swar-12 149769112 7.928 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-32B/scalar-12 44041342 27.13 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/hit-32B/swar-12 138732252 8.758 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/hit-32B/scalar-12 33871633 30.73 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/hit-tail/swar-12 100000000 12.34 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/hit-tail/scalar-12 16138168 75.39 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-64B/swar-12 100000000 11.37 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-64B/scalar-12 17902330 67.78 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-512B/swar-12 17640939 67.65 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/miss-512B/scalar-12 2211582 544.4 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/long-hit-64B/swar-12 100000000 11.58 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/long-hit-64B/scalar-12 23906326 51.33 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/long-miss-64B/swar-12 100000000 11.33 ns/op 0 B/op 0 allocs/op +Benchmark_IndexFold/long-miss-64B/scalar-12 23553600 50.27 ns/op 0 B/op 0 allocs/op + +# ASCII +Benchmark_IsASCII/7B/swar-12 238699563 5.001 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/7B/scalar-12 292669958 4.064 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/8B/swar-12 439961722 2.687 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/8B/scalar-12 274732192 4.364 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/16B/swar-12 367179319 3.255 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/16B/scalar-12 179331987 6.706 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/32B/swar-12 368345228 3.271 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/32B/scalar-12 100000000 11.62 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/64B/swar-12 265505815 4.486 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/64B/scalar-12 57864440 21.33 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/512B/swar-12 47664915 25.14 ns/op 0 B/op 0 allocs/op +Benchmark_IsASCII/512B/scalar-12 7081790 169.5 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/7B/swar-12 142234436 8.424 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/7B/scalar-12 142394628 8.174 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/8B/swar-12 401721262 3.099 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/8B/scalar-12 138047568 8.654 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/16B/swar-12 244592613 5.059 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/16B/scalar-12 75413396 15.94 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/32B/swar-12 138857455 8.875 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/32B/scalar-12 39539425 30.18 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/64B/swar-12 75193269 16.46 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/64B/scalar-12 20898807 57.81 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/512B/swar-12 9634008 124.9 ns/op 0 B/op 0 allocs/op +Benchmark_IndexNonQuotable/512B/scalar-12 2619062 460.6 ns/op 0 B/op 0 allocs/op # Trim -Benchmark_TrimRight/fiber-12 556198228 2.195 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRight/default-12 394761454 3.080 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRightBytes/fiber-12 552028600 2.188 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRightBytes/default-12 364481850 3.382 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeft/fiber-12 493763746 2.439 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeft/default-12 394974064 3.147 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeftBytes/fiber-12 533978953 2.129 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeftBytes/default-12 390162962 3.090 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/fiber-12 303470884 4.120 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/default-12 238874899 4.889 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/default.trimspace-12 230900814 5.395 ns/op 0 B/op 0 allocs/op -Benchmark_TrimBytes/fiber-12 302062202 3.963 ns/op 0 B/op 0 allocs/op -Benchmark_TrimBytes/default-12 245799558 5.052 ns/op 0 B/op 0 allocs/op -Benchmark_TrimBytes/default.trimspace-12 217547649 5.520 ns/op 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/empty-12 1000000000 0.3031 ns/op 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/empty-12 554941630 2.211 ns/op 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/spaces-12 378650127 3.139 ns/op 955.63 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/spaces-12 313411838 3.929 ns/op 763.64 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/ascii-word-12 325986266 3.682 ns/op 2444.19 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/ascii-word-12 260590963 4.751 ns/op 1894.19 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/auth-header-bearer-12 278654619 4.279 ns/op 5608.46 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/auth-header-bearer-12 217345236 5.665 ns/op 4236.48 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/auth-header-basic-12 374732025 3.245 ns/op 11711.17 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/auth-header-basic-12 245032184 5.138 ns/op 7395.35 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/accept-encoding-12 322936750 3.772 ns/op 5567.24 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/accept-encoding-12 259551775 4.791 ns/op 4383.20 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/content-type-12 327816716 3.700 ns/op 5404.76 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/content-type-12 261473678 4.851 ns/op 4122.61 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/x-forwarded-for-12 430371238 3.136 ns/op 8929.40 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/x-forwarded-for-12 259204014 4.351 ns/op 6434.58 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/query-params-12 297618063 3.686 ns/op 5425.87 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/query-params-12 260095944 4.591 ns/op 4356.73 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/ascii-long-12 244683963 4.594 ns/op 22203.55 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/ascii-long-12 196174795 6.097 ns/op 16728.77 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/no-trim-12 1000000000 0.6452 ns/op 7750.12 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/no-trim-12 431454468 2.758 ns/op 1813.07 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/fiber/mixed-whitespace-12 279402585 4.522 ns/op 3759.80 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpace/default/mixed-whitespace-12 216711007 5.540 ns/op 3068.59 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/empty-12 1000000000 0.3096 ns/op 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/empty-12 486190254 2.466 ns/op 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/spaces-12 350271897 3.119 ns/op 961.83 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/spaces-12 294683720 4.188 ns/op 716.36 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/ascii-word-12 328983026 3.690 ns/op 2438.91 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/ascii-word-12 242798146 5.181 ns/op 1737.10 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/auth-header-bearer-12 276373491 4.299 ns/op 5582.44 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/auth-header-bearer-12 203176566 6.066 ns/op 3956.37 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/auth-header-basic-12 371498816 3.203 ns/op 11863.40 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/auth-header-basic-12 233269664 5.123 ns/op 7417.55 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/accept-encoding-12 331386438 3.740 ns/op 5614.74 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/accept-encoding-12 248029371 5.101 ns/op 4116.44 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/content-type-12 324414853 3.624 ns/op 5518.54 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/content-type-12 250866906 4.764 ns/op 4198.35 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/x-forwarded-for-12 448704225 2.690 ns/op 10409.74 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/x-forwarded-for-12 267864716 5.395 ns/op 5189.83 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/query-params-12 298958100 3.821 ns/op 5234.49 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/query-params-12 247046188 4.859 ns/op 4116.00 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/ascii-long-12 259565295 4.594 ns/op 22201.52 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/ascii-long-12 188316823 6.343 ns/op 16079.55 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/no-trim-12 1000000000 0.6094 ns/op 8204.80 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/no-trim-12 395805396 3.032 ns/op 1649.01 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/fiber/mixed-whitespace-12 281619982 4.206 ns/op 4041.49 MB/s 0 B/op 0 allocs/op -Benchmark_TrimSpaceBytes/default/mixed-whitespace-12 209473969 5.716 ns/op 2974.30 MB/s 0 B/op 0 allocs/op +Benchmark_TrimRight/fiber-12 587360972 2.043 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRight/default-12 417142952 2.887 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRightBytes/fiber-12 590828253 2.067 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRightBytes/default-12 374548395 3.236 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeft/fiber-12 590822072 2.030 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeft/default-12 408184784 2.967 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeftBytes/fiber-12 592875732 2.033 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeftBytes/default-12 405921434 2.967 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/fiber-12 317625432 3.758 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/default-12 250173384 4.735 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/default.trimspace-12 241883282 5.022 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/fiber-12 308885211 3.858 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/default-12 254064188 4.802 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/default.trimspace-12 224231778 5.330 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/empty-12 1000000000 0.3017 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/empty-12 583064172 2.074 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/spaces-12 387354229 3.059 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/spaces-12 320059388 3.651 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/ascii-word-12 345078337 3.535 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/ascii-word-12 270603267 4.465 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/auth-header-bearer-12 291494109 4.141 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/auth-header-bearer-12 224087944 5.329 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/auth-header-basic-12 390765345 3.047 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/auth-header-basic-12 258943531 4.718 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/accept-encoding-12 344369745 3.490 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/accept-encoding-12 277203638 4.347 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/content-type-12 344263705 3.506 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/content-type-12 276213367 4.343 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/x-forwarded-for-12 460119685 2.653 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/x-forwarded-for-12 296468840 4.058 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/query-params-12 330053276 3.520 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/query-params-12 275688495 4.352 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/ascii-long-12 275511898 4.342 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/ascii-long-12 206143018 5.807 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/no-trim-12 1000000000 0.5914 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/no-trim-12 451842741 2.634 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/fiber/mixed-whitespace-12 296083767 4.054 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpace/default/mixed-whitespace-12 224007750 5.294 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/empty-12 1000000000 0.2914 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/empty-12 519488766 2.352 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/spaces-12 410959255 2.966 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/spaces-12 315602949 3.827 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/ascii-word-12 341217429 3.519 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/ascii-word-12 247799318 4.667 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/auth-header-bearer-12 295130076 4.182 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/auth-header-bearer-12 209162235 5.685 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/auth-header-basic-12 393366796 3.053 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/auth-header-basic-12 240882009 4.930 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/accept-encoding-12 343673244 3.512 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/accept-encoding-12 250962042 4.642 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/content-type-12 344382758 3.516 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/content-type-12 250622116 4.693 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/x-forwarded-for-12 459435145 2.621 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/x-forwarded-for-12 276595178 4.359 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/query-params-12 341144192 3.526 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/query-params-12 255843174 4.873 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/ascii-long-12 270183530 4.445 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/ascii-long-12 189740618 6.335 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/no-trim-12 1000000000 0.5969 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/no-trim-12 406072189 2.941 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/fiber/mixed-whitespace-12 289382116 4.057 ns/op 0 B/op 0 allocs/op +Benchmark_TrimSpaceBytes/default/mixed-whitespace-12 218559462 5.493 ns/op 0 B/op 0 allocs/op # Convert -Benchmark_ConvertToBytes/fiber-12 305651658 3.974 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/int-12 494480786 2.455 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/int8-12 601754490 2.009 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/int16-12 480638928 2.507 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/int32-12 483084573 2.505 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/int64-12 485630560 2.572 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/uint-12 542968509 2.126 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/uint8-12 603426201 2.082 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/uint16-12 568713753 2.111 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/uint32-12 567709288 2.106 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/uint64-12 568810574 2.180 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/string-12 606042240 2.191 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/[]uint8-12 94285881 12.78 ns/op 16 B/op 1 allocs/op -Benchmark_ToString/bool-12 601565197 1.995 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/float32-12 24979378 48.10 ns/op 4 B/op 1 allocs/op -Benchmark_ToString/float64-12 17121148 70.77 ns/op 4 B/op 1 allocs/op -Benchmark_ToString/time.Time-12 12894415 93.74 ns/op 24 B/op 1 allocs/op -Benchmark_ToString/time.Time#01-12 12970261 95.06 ns/op 24 B/op 1 allocs/op -Benchmark_ToString/[]string-12 52706324 26.62 ns/op 16 B/op 1 allocs/op -Benchmark_ToString/[]int-12 51370597 24.19 ns/op 8 B/op 1 allocs/op -Benchmark_ToString/[2]int-12 31207941 38.93 ns/op 16 B/op 1 allocs/op -Benchmark_ToString/[][]int-12 8454266 142.9 ns/op 112 B/op 6 allocs/op -Benchmark_ToString/[]interface_{}-12 9656547 133.4 ns/op 72 B/op 3 allocs/op -Benchmark_ToString/utils.MyStringer-12 390543688 3.060 ns/op 0 B/op 0 allocs/op -Benchmark_ToString/utils.CustomType-12 11184109 106.9 ns/op 16 B/op 1 allocs/op -Benchmark_ToString_concurrency/int-12 1000000000 0.3039 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/int8-12 1000000000 0.2423 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/int16-12 1000000000 0.2901 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/int32-12 1000000000 0.2935 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/int64-12 1000000000 0.2856 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/uint-12 1000000000 0.3382 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/uint8-12 1000000000 0.2279 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/uint16-12 1000000000 0.2460 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/uint32-12 1000000000 0.2469 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/uint64-12 1000000000 0.2615 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/string-12 1000000000 0.2305 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/[]uint8-12 176655307 6.047 ns/op 16 B/op 1 allocs/op -Benchmark_ToString_concurrency/bool-12 1000000000 0.2306 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/float32-12 183723876 8.276 ns/op 4 B/op 1 allocs/op -Benchmark_ToString_concurrency/float64-12 121188367 9.445 ns/op 4 B/op 1 allocs/op -Benchmark_ToString_concurrency/time.Time-12 68860974 17.98 ns/op 24 B/op 1 allocs/op -Benchmark_ToString_concurrency/time.Time#01-12 65536016 18.02 ns/op 24 B/op 1 allocs/op -Benchmark_ToString_concurrency/[]string-12 159027694 8.212 ns/op 16 B/op 1 allocs/op -Benchmark_ToString_concurrency/[]int-12 203055362 5.631 ns/op 8 B/op 1 allocs/op -Benchmark_ToString_concurrency/[2]int-12 131550500 9.186 ns/op 16 B/op 1 allocs/op -Benchmark_ToString_concurrency/[][]int-12 21128588 55.64 ns/op 112 B/op 6 allocs/op -Benchmark_ToString_concurrency/[]interface_{}-12 30081375 38.26 ns/op 72 B/op 3 allocs/op -Benchmark_ToString_concurrency/utils.MyStringer-12 1000000000 0.3727 ns/op 0 B/op 0 allocs/op -Benchmark_ToString_concurrency/utils.CustomType-12 49417796 29.59 ns/op 16 B/op 1 allocs/op -Benchmark_UnsafeBytes/unsafe-12 1000000000 0.5450 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeBytes/default-12 80875700 14.54 ns/op 16 B/op 1 allocs/op -Benchmark_UnsafeString/unsafe-12 1000000000 0.3579 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeString/default-12 99855757 12.15 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/0-12 394155170 3.023 ns/op 0 B/op 0 allocs/op -Benchmark_ByteSize/1-12 64058362 18.38 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/500-12 58149043 21.64 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1024-12 58947781 20.26 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1126-12 52060172 23.14 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1048576-12 60832776 19.66 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1153024-12 51995977 22.68 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1073741824-12 61154545 19.64 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1180696576-12 52380780 23.06 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1099511627776-12 62655552 19.10 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1209033293824-12 53917438 23.08 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1125899906842624-12 60970962 18.98 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1238050092875776-12 55339809 21.64 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1152921504606846976-12 63578542 18.73 ns/op 16 B/op 1 allocs/op -Benchmark_ByteSize/1267763295104794624-12 55347254 21.74 ns/op 16 B/op 1 allocs/op +Benchmark_ConvertToBytes/fiber-12 320332576 3.787 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/int-12 499429904 2.382 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/int8-12 614406423 1.965 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/int16-12 486644316 2.463 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/int32-12 495227163 2.422 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/int64-12 502362236 2.429 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/uint-12 561693333 2.101 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/uint8-12 611239028 1.965 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/uint16-12 573383422 2.127 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/uint32-12 575723191 2.093 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/uint64-12 575901750 2.089 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/string-12 613909396 1.959 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/[]uint8-12 96832432 12.11 ns/op 16 B/op 1 allocs/op +Benchmark_ToString/bool-12 608058547 1.990 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/float32-12 23195016 47.11 ns/op 4 B/op 1 allocs/op +Benchmark_ToString/float64-12 17204126 70.24 ns/op 4 B/op 1 allocs/op +Benchmark_ToString/time.Time-12 13103935 91.85 ns/op 24 B/op 1 allocs/op +Benchmark_ToString/time.Time#01-12 13198113 91.22 ns/op 24 B/op 1 allocs/op +Benchmark_ToString/[]string-12 65187865 18.35 ns/op 16 B/op 1 allocs/op +Benchmark_ToString/[]int-12 54417859 21.53 ns/op 8 B/op 1 allocs/op +Benchmark_ToString/[2]int-12 32691942 36.57 ns/op 16 B/op 1 allocs/op +Benchmark_ToString/[][]int-12 9382754 128.8 ns/op 112 B/op 6 allocs/op +Benchmark_ToString/[]interface_{}-12 10584483 114.5 ns/op 72 B/op 3 allocs/op +Benchmark_ToString/utils.MyStringer-12 365979882 3.277 ns/op 0 B/op 0 allocs/op +Benchmark_ToString/utils.CustomType-12 11649330 102.5 ns/op 16 B/op 1 allocs/op +Benchmark_ToString_concurrency/int-12 1000000000 0.2619 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/int8-12 1000000000 0.2202 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/int16-12 1000000000 0.2708 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/int32-12 1000000000 0.2658 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/int64-12 1000000000 0.2658 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/uint-12 1000000000 0.2281 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/uint8-12 1000000000 0.2223 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/uint16-12 1000000000 0.2396 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/uint32-12 1000000000 0.2273 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/uint64-12 1000000000 0.2271 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/string-12 1000000000 0.2117 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/[]uint8-12 213842515 5.718 ns/op 16 B/op 1 allocs/op +Benchmark_ToString_concurrency/bool-12 1000000000 0.2118 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/float32-12 201081819 6.011 ns/op 4 B/op 1 allocs/op +Benchmark_ToString_concurrency/float64-12 142000966 8.799 ns/op 4 B/op 1 allocs/op +Benchmark_ToString_concurrency/time.Time-12 71451783 18.04 ns/op 24 B/op 1 allocs/op +Benchmark_ToString_concurrency/time.Time#01-12 69313578 17.74 ns/op 24 B/op 1 allocs/op +Benchmark_ToString_concurrency/[]string-12 187564670 6.455 ns/op 16 B/op 1 allocs/op +Benchmark_ToString_concurrency/[]int-12 262707805 4.498 ns/op 8 B/op 1 allocs/op +Benchmark_ToString_concurrency/[2]int-12 129274200 8.777 ns/op 16 B/op 1 allocs/op +Benchmark_ToString_concurrency/[][]int-12 22638411 52.65 ns/op 112 B/op 6 allocs/op +Benchmark_ToString_concurrency/[]interface_{}-12 34120837 35.36 ns/op 72 B/op 3 allocs/op +Benchmark_ToString_concurrency/utils.MyStringer-12 1000000000 0.4259 ns/op 0 B/op 0 allocs/op +Benchmark_ToString_concurrency/utils.CustomType-12 54806094 25.95 ns/op 16 B/op 1 allocs/op +Benchmark_UnsafeBytes/unsafe-12 1000000000 0.5658 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeBytes/default-12 88407558 13.50 ns/op 16 B/op 1 allocs/op +Benchmark_UnsafeString/unsafe-12 1000000000 0.3545 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeString/default-12 100000000 12.09 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/0-12 395954607 3.023 ns/op 0 B/op 0 allocs/op +Benchmark_ByteSize/1-12 65295756 18.15 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/500-12 58264801 20.92 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1024-12 60886531 19.80 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1126-12 53409986 22.69 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1048576-12 60969927 19.41 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1153024-12 53647869 22.12 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1073741824-12 63792180 19.05 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1180696576-12 54560540 22.02 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1099511627776-12 64080169 18.85 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1209033293824-12 52957434 22.01 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1125899906842624-12 65629659 18.33 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1238050092875776-12 56535770 21.73 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1152921504606846976-12 64569356 18.56 ns/op 16 B/op 1 allocs/op +Benchmark_ByteSize/1267763295104794624-12 56481996 21.10 ns/op 16 B/op 1 allocs/op # Format and Append -Benchmark_FormatUint/small/fiber-12 1000000000 0.3025 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint/small/strconv-12 584345623 2.084 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint/medium/fiber-12 125524198 10.04 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint/medium/strconv-12 57687223 21.13 ns/op 16 B/op 1 allocs/op -Benchmark_FormatUint/large/fiber-12 64259034 18.67 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint/large/strconv-12 44394020 26.98 ns/op 24 B/op 1 allocs/op -Benchmark_FormatInt/small_pos/fiber-12 609931978 1.994 ns/op 0 B/op 0 allocs/op -Benchmark_FormatInt/small_pos/strconv-12 579927732 2.046 ns/op 0 B/op 0 allocs/op -Benchmark_FormatInt/small_neg/fiber-12 611030502 1.969 ns/op 0 B/op 0 allocs/op -Benchmark_FormatInt/small_neg/strconv-12 82791925 14.13 ns/op 3 B/op 1 allocs/op -Benchmark_FormatInt/medium_pos/fiber-12 61593342 19.56 ns/op 16 B/op 1 allocs/op -Benchmark_FormatInt/medium_pos/strconv-12 58425434 21.66 ns/op 16 B/op 1 allocs/op -Benchmark_FormatInt/medium_neg/fiber-12 60677822 19.54 ns/op 16 B/op 1 allocs/op -Benchmark_FormatInt/medium_neg/strconv-12 59446896 20.23 ns/op 16 B/op 1 allocs/op -Benchmark_FormatInt/large_pos/fiber-12 46969258 25.61 ns/op 24 B/op 1 allocs/op -Benchmark_FormatInt/large_pos/strconv-12 44999648 26.57 ns/op 24 B/op 1 allocs/op -Benchmark_FormatInt/large_neg/fiber-12 45581593 26.18 ns/op 24 B/op 1 allocs/op -Benchmark_FormatInt/large_neg/strconv-12 45468108 26.83 ns/op 24 B/op 1 allocs/op -Benchmark_FormatUint32/fiber-12 126955321 9.411 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint32/strconv-12 58030117 21.34 ns/op 16 B/op 1 allocs/op -Benchmark_FormatInt32/fiber-12 60967604 19.60 ns/op 16 B/op 1 allocs/op -Benchmark_FormatInt32/strconv-12 59480410 20.12 ns/op 16 B/op 1 allocs/op -Benchmark_FormatUint16/fiber-12 164073385 7.314 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint16/strconv-12 74532424 16.78 ns/op 5 B/op 1 allocs/op -Benchmark_FormatInt16/fiber-12 74833182 15.94 ns/op 8 B/op 1 allocs/op -Benchmark_FormatInt16/strconv-12 75867136 16.05 ns/op 8 B/op 1 allocs/op -Benchmark_FormatUint8/fiber-12 1000000000 0.2999 ns/op 0 B/op 0 allocs/op -Benchmark_FormatUint8/strconv-12 82306642 14.64 ns/op 3 B/op 1 allocs/op -Benchmark_FormatInt8/fiber-12 1000000000 0.3015 ns/op 0 B/op 0 allocs/op -Benchmark_FormatInt8/strconv-12 80331591 14.71 ns/op 4 B/op 1 allocs/op -Benchmark_AppendUint/fiber-12 135872859 8.812 ns/op 0 B/op 0 allocs/op -Benchmark_AppendUint/strconv-12 100000000 10.44 ns/op 0 B/op 0 allocs/op -Benchmark_AppendInt/small_neg/fiber-12 345227787 3.321 ns/op 0 B/op 0 allocs/op -Benchmark_AppendInt/small_neg/strconv-12 210340929 5.744 ns/op 0 B/op 0 allocs/op -Benchmark_AppendInt/medium_neg/fiber-12 121853198 9.739 ns/op 0 B/op 0 allocs/op -Benchmark_AppendInt/medium_neg/strconv-12 100000000 10.04 ns/op 0 B/op 0 allocs/op +Benchmark_FormatUint/small/fiber-12 607527520 1.966 ns/op 0 B/op 0 allocs/op +Benchmark_FormatUint/small/strconv-12 607521366 1.973 ns/op 0 B/op 0 allocs/op +Benchmark_FormatUint/medium/fiber-12 64427509 19.04 ns/op 16 B/op 1 allocs/op +Benchmark_FormatUint/medium/strconv-12 59566404 20.42 ns/op 16 B/op 1 allocs/op +Benchmark_FormatUint/large/fiber-12 45395149 26.16 ns/op 24 B/op 1 allocs/op +Benchmark_FormatUint/large/strconv-12 45620366 26.38 ns/op 24 B/op 1 allocs/op +Benchmark_FormatInt/small_pos/fiber-12 608898928 1.963 ns/op 0 B/op 0 allocs/op +Benchmark_FormatInt/small_pos/strconv-12 598721480 1.976 ns/op 0 B/op 0 allocs/op +Benchmark_FormatInt/small_neg/fiber-12 616958124 1.946 ns/op 0 B/op 0 allocs/op +Benchmark_FormatInt/small_neg/strconv-12 87181050 13.77 ns/op 3 B/op 1 allocs/op +Benchmark_FormatInt/medium_pos/fiber-12 63105722 19.12 ns/op 16 B/op 1 allocs/op +Benchmark_FormatInt/medium_pos/strconv-12 57454753 20.70 ns/op 16 B/op 1 allocs/op +Benchmark_FormatInt/medium_neg/fiber-12 63631222 19.10 ns/op 16 B/op 1 allocs/op +Benchmark_FormatInt/medium_neg/strconv-12 61296942 20.17 ns/op 16 B/op 1 allocs/op +Benchmark_FormatInt/large_pos/fiber-12 48604558 25.08 ns/op 24 B/op 1 allocs/op +Benchmark_FormatInt/large_pos/strconv-12 45605844 26.25 ns/op 24 B/op 1 allocs/op +Benchmark_FormatInt/large_neg/fiber-12 47388217 25.09 ns/op 24 B/op 1 allocs/op +Benchmark_FormatInt/large_neg/strconv-12 45871998 26.10 ns/op 24 B/op 1 allocs/op +Benchmark_FormatUint32/fiber-12 63452196 18.63 ns/op 16 B/op 1 allocs/op +Benchmark_FormatUint32/strconv-12 59485942 20.08 ns/op 16 B/op 1 allocs/op +Benchmark_FormatInt32/fiber-12 63030724 18.98 ns/op 16 B/op 1 allocs/op +Benchmark_FormatInt32/strconv-12 61887302 19.69 ns/op 16 B/op 1 allocs/op +Benchmark_FormatUint16/fiber-12 80149611 14.48 ns/op 5 B/op 1 allocs/op +Benchmark_FormatUint16/strconv-12 77022440 15.43 ns/op 5 B/op 1 allocs/op +Benchmark_FormatInt16/fiber-12 79013646 15.23 ns/op 8 B/op 1 allocs/op +Benchmark_FormatInt16/strconv-12 79695165 15.24 ns/op 8 B/op 1 allocs/op +Benchmark_FormatUint8/fiber-12 599811685 1.947 ns/op 0 B/op 0 allocs/op +Benchmark_FormatUint8/strconv-12 84144096 14.18 ns/op 3 B/op 1 allocs/op +Benchmark_FormatInt8/fiber-12 630717244 1.985 ns/op 0 B/op 0 allocs/op +Benchmark_FormatInt8/strconv-12 83826201 14.24 ns/op 4 B/op 1 allocs/op +Benchmark_AppendUint/fiber-12 130817422 9.139 ns/op 0 B/op 0 allocs/op +Benchmark_AppendUint/strconv-12 100000000 10.08 ns/op 0 B/op 0 allocs/op +Benchmark_AppendInt/small_neg/fiber-12 369512403 3.266 ns/op 0 B/op 0 allocs/op +Benchmark_AppendInt/small_neg/strconv-12 213341739 5.627 ns/op 0 B/op 0 allocs/op +Benchmark_AppendInt/medium_neg/fiber-12 125937435 9.531 ns/op 0 B/op 0 allocs/op +Benchmark_AppendInt/medium_neg/strconv-12 122490632 9.791 ns/op 0 B/op 0 allocs/op # Token -Benchmark_GenerateSecureToken/16_bytes-12 4445361 284.0 ns/op 24 B/op 1 allocs/op -Benchmark_GenerateSecureToken/32_bytes-12 4071276 291.0 ns/op 48 B/op 1 allocs/op -Benchmark_TokenGenerators/UUIDv4-12 4021219 327.0 ns/op 64 B/op 2 allocs/op -Benchmark_TokenGenerators/SecureToken-12 4073481 287.3 ns/op 48 B/op 1 allocs/op +Benchmark_GenerateSecureToken/16_bytes-12 4565527 258.6 ns/op 24 B/op 1 allocs/op +Benchmark_GenerateSecureToken/32_bytes-12 4306404 280.8 ns/op 48 B/op 1 allocs/op +Benchmark_TokenGenerators/UUIDv4-12 4019139 288.2 ns/op 64 B/op 2 allocs/op +Benchmark_TokenGenerators/SecureToken-12 4360036 276.9 ns/op 48 B/op 1 allocs/op # HTTP -Benchmark_GetMIME/fiber-12 29202141 41.13 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default-12 16953927 73.52 ns/op 0 B/op 0 allocs/op -Benchmark_ParseVendorSpecificContentType/vendorContentType-12 154110751 7.784 ns/op 0 B/op 0 allocs/op -Benchmark_ParseVendorSpecificContentType/defaultContentType-12 396583869 3.023 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/fiber-12 1000000000 0.3396 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/default-12 447403729 2.694 ns/op 0 B/op 0 allocs/op +Benchmark_GetMIME/fiber-12 22321272 53.90 ns/op 0 B/op 0 allocs/op +Benchmark_GetMIME/default-12 17492530 68.46 ns/op 0 B/op 0 allocs/op +Benchmark_ParseVendorSpecificContentType/vendorContentType-12 125467813 9.619 ns/op 0 B/op 0 allocs/op +Benchmark_ParseVendorSpecificContentType/defaultContentType-12 404169172 2.974 ns/op 0 B/op 0 allocs/op +Benchmark_StatusMessage/fiber-12 1000000000 0.3349 ns/op 0 B/op 0 allocs/op +Benchmark_StatusMessage/default-12 460456902 2.610 ns/op 0 B/op 0 allocs/op # IP -Benchmark_IsIPv4/fiber-12 82770516 14.99 ns/op 0 B/op 0 allocs/op -Benchmark_IsIPv4/default-12 52222729 22.87 ns/op 0 B/op 0 allocs/op -Benchmark_IsIPv6/fiber-12 27058488 44.28 ns/op 0 B/op 0 allocs/op -Benchmark_IsIPv6/default-12 19518978 63.64 ns/op 0 B/op 0 allocs/op +Benchmark_IsIPv4/fiber-12 80408070 14.58 ns/op 0 B/op 0 allocs/op +Benchmark_IsIPv4/default-12 54025154 21.99 ns/op 0 B/op 0 allocs/op +Benchmark_IsIPv6/fiber-12 28283431 42.67 ns/op 0 B/op 0 allocs/op +Benchmark_IsIPv6/default-12 20747964 58.18 ns/op 0 B/op 0 allocs/op # Parse -Benchmark_ParseUint/fiber-12 171968007 6.984 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint/fiber_bytes-12 162498577 7.444 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint/default-12 87571795 13.70 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt/fiber-12 186597552 6.331 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt/fiber_bytes-12 180017686 6.686 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt/default-12 76678334 15.73 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt32/fiber-12 155367661 7.717 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt32/fiber_bytes-12 144681208 8.325 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt32/default-12 76537323 15.73 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt16/fiber-12 228710796 5.222 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt16/fiber_bytes-12 223933951 5.322 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt16/default-12 100000000 11.05 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt8/fiber-12 257930811 4.626 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt8/fiber_bytes-12 240602468 4.588 ns/op 0 B/op 0 allocs/op -Benchmark_ParseInt8/default-12 138314644 8.609 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint32/fiber-12 170833845 7.138 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint32/fiber_bytes-12 162105060 7.443 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint32/default-12 85408747 13.70 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint16/fiber-12 262682311 4.547 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint16/fiber_bytes-12 257359222 4.658 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint16/default-12 134532823 8.902 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint8/fiber-12 350422557 3.416 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint8/fiber_bytes-12 327501829 3.700 ns/op 0 B/op 0 allocs/op -Benchmark_ParseUint8/default-12 182633575 6.816 ns/op 0 B/op 0 allocs/op -Benchmark_ParseFloat64/fiber-12 100000000 10.73 ns/op 0 B/op 0 allocs/op -Benchmark_ParseFloat64/fiber_bytes-12 100000000 10.86 ns/op 0 B/op 0 allocs/op -Benchmark_ParseFloat64/default-12 50053615 24.18 ns/op 0 B/op 0 allocs/op -Benchmark_ParseFloat32/fiber-12 100000000 11.62 ns/op 0 B/op 0 allocs/op -Benchmark_ParseFloat32/fiber_bytes-12 100000000 11.97 ns/op 0 B/op 0 allocs/op -Benchmark_ParseFloat32/default-12 46590180 25.98 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint/fiber-12 236748204 5.049 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint/fiber_bytes-12 224650773 5.348 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint/default-12 88588122 13.75 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint_DigitRuns/6digit-12 207540178 5.769 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint_DigitRuns/8digit-12 222360716 5.403 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint_DigitRuns/12digit-12 156132950 7.699 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint_DigitRuns/19digit-12 135049720 8.880 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint_DigitRuns/1digit-12 338642533 3.553 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint_DigitRuns/4digit-12 212370570 5.672 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt/fiber-12 299791800 3.981 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt/fiber_bytes-12 288714712 4.165 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt/default-12 76370292 15.48 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt32/fiber-12 200451391 5.988 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt32/fiber_bytes-12 201629558 5.956 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt32/default-12 78384832 15.71 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt16/fiber-12 220670613 5.462 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt16/fiber_bytes-12 220175068 5.430 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt16/default-12 100000000 10.71 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt8/fiber-12 277948545 4.279 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt8/fiber_bytes-12 264784368 4.566 ns/op 0 B/op 0 allocs/op +Benchmark_ParseInt8/default-12 142184748 8.373 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint32/fiber-12 238916116 5.025 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint32/fiber_bytes-12 222286592 5.409 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint32/default-12 90395479 13.33 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint16/fiber-12 251398184 4.828 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint16/fiber_bytes-12 248772012 4.838 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint16/default-12 137015901 8.788 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint8/fiber-12 360430984 3.427 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint8/fiber_bytes-12 327526336 3.659 ns/op 0 B/op 0 allocs/op +Benchmark_ParseUint8/default-12 181904056 6.438 ns/op 0 B/op 0 allocs/op +Benchmark_ParseFloat64/fiber-12 82912768 14.76 ns/op 0 B/op 0 allocs/op +Benchmark_ParseFloat64/fiber_bytes-12 95692504 12.68 ns/op 0 B/op 0 allocs/op +Benchmark_ParseFloat64/default-12 50109265 25.05 ns/op 0 B/op 0 allocs/op +Benchmark_ParseFloat32/fiber-12 76648135 15.76 ns/op 0 B/op 0 allocs/op +Benchmark_ParseFloat32/fiber_bytes-12 86422119 13.63 ns/op 0 B/op 0 allocs/op +Benchmark_ParseFloat32/default-12 47198646 25.41 ns/op 0 B/op 0 allocs/op # Time -Benchmark_CalculateTimestamp/fiber-12 1000000000 0.3029 ns/op 0 B/op 0 allocs/op -Benchmark_CalculateTimestamp/default-12 36542426 32.77 ns/op 0 B/op 0 allocs/op -Benchmark_CalculateTimestamp/fiber_asserted-12 4240807 276.8 ns/op 13 B/op 2 allocs/op -Benchmark_CalculateTimestamp/default_asserted-12 3974042 305.6 ns/op 8 B/op 2 allocs/op +Benchmark_CalculateTimestamp/fiber-12 1000000000 0.3366 ns/op 0 B/op 0 allocs/op +Benchmark_CalculateTimestamp/default-12 36163686 32.69 ns/op 0 B/op 0 allocs/op +Benchmark_CalculateTimestamp/fiber_asserted-12 4471299 267.8 ns/op 12 B/op 2 allocs/op +Benchmark_CalculateTimestamp/default_asserted-12 3991147 298.7 ns/op 8 B/op 2 allocs/op # XML -Benchmark_GolangXMLEncoder-12 591470 2025 ns/op 4864 B/op 12 allocs/op -Benchmark_DefaultXMLEncoder-12 563848 2124 ns/op 4864 B/op 12 allocs/op -Benchmark_DefaultXMLDecoder-12 329698 3513 ns/op 2857 B/op 57 allocs/op +Benchmark_GolangXMLEncoder-12 574852 2040 ns/op 4864 B/op 12 allocs/op +Benchmark_DefaultXMLEncoder-12 593738 2012 ns/op 4864 B/op 12 allocs/op +Benchmark_DefaultXMLDecoder-12 309031 3876 ns/op 2892 B/op 57 allocs/op ``` See all the benchmarks under +## Case-insensitive matching helpers + +`EqualFold`, `IndexFold`, `ContainsFold`, `HasPrefixFold`, and +`HasSuffixFold` compare ASCII case-insensitively using the SWAR word +loops: only `A`..`Z`/`a`..`z` fold, every other byte (including bytes +>= 0x80) must match exactly. `HasPrefixFold` and `HasSuffixFold` cover +the header checks Fiber otherwise spells as an allocation-prone +`ToLower` + `HasPrefix`/`HasSuffix` pair (authorization schemes such as +`Bearer`, content-type prefixes such as `multipart/form-data`, suffixes +such as `+json`), and like the other Fold helpers they take the needle +as a plain string because call sites pass constant tokens. Their +`Benchmark_HasPrefixFold`/`Benchmark_HasSuffixFold` numbers are tracked +on the [benchmark charts](https://gofiber.github.io/utils/benchmarks/) +and join the catalog above on its next regeneration. + +## SWAR primitives + +The [`swar`](swar/) package exports the SWAR (SIMD within a register) +building blocks the helpers above are composed from: `Load8`/`Store8`, +`Broadcast`, `ZeroLanes`, `MatchByteMask`/`MatchRangeMask`, +`ToLowerWord`/`ToUpperWord`, `FirstLane`/`LastLane`, and the `WordLen`, +`Ones`, `HighBits`, and `LowSeven` constants. They are exported so +downstream packages (Fiber itself, middleware) can fuse their own byte +scans — for example, finding a delimiter while classifying the bytes +before it — without re-deriving the bit tricks. The contracts (unchecked +bounds preconditions, `ZeroLanes`' approximate mask, the little-endian +lane order) and runnable examples live in the package documentation. + +The package benchmarks its primitives against their stdlib counterparts +the same way the helpers above do, measuring the canonical loops composed +from them: `Load8`/`Store8` vs `encoding/binary`'s little-endian +`Uint64`/`PutUint64`, a `ZeroLanes` first-match scan vs +`strings.IndexByte`, a `MatchByteMask`+`LastLane` reverse scan vs +`bytes.LastIndexByte`, a `MatchRangeMask` digit scan vs +`strings.IndexAny`, and an in-place `ToLowerWord` loop vs +`bytes.ToLower`. The composed loops are pinned to the stdlib results by a +dedicated test, and the numbers are tracked per commit on the +[benchmark charts](https://gofiber.github.io/utils/benchmarks/). Note +that `strings.IndexByte` is hand-written SIMD assembly and overtakes the +portable SWAR scan on large inputs; the SWAR primitives earn their keep +on short HTTP-sized inputs and on fused scans the stdlib has no single +function for. The package's remaining benchmark, `Benchmark_Load8_Fusion`, +is an advisory codegen guard rather than an API performance promise, so +it is deliberately not part of the catalog above. + ## ☕ Supporters Fiber is an open-source project that runs on donations to pay the bills, e.g., our domain name, hosting, and serverless infrastructure. If you want to support Fiber, please become a [GitHub Sponsor](https://github.com/sponsors/gofiber). diff --git a/src/vendor/github.com/gofiber/utils/v2/ascii.go b/src/vendor/github.com/gofiber/utils/v2/ascii.go new file mode 100644 index 00000000..a16018c7 --- /dev/null +++ b/src/vendor/github.com/gofiber/utils/v2/ascii.go @@ -0,0 +1,104 @@ +package utils + +import ( + "github.com/gofiber/utils/v2/swar" +) + +// quoteLanes and backslashLanes broadcast '"' and '\\' to every lane. +const ( + quoteLanes = uint64('"') * swar.Ones + backslashLanes = uint64('\\') * swar.Ones +) + +// IsASCII reports whether s contains only ASCII bytes (no byte >= 0x80). +// It ORs four words per iteration where possible (no index is needed, so +// detection can be deferred to one test per 32 bytes), then tests word-wise; +// inputs of 8+ bytes finish with one overlapping word at n-8, shorter ones +// byte-wise. +func IsASCII[S byteSeq](s S) bool { + n := len(s) + i := 0 + for ; i+32 <= n; i += 32 { + acc := swar.Load8(s, i) | swar.Load8(s, i+8) | swar.Load8(s, i+16) | swar.Load8(s, i+24) + if acc&swar.HighBits != 0 { + return false + } + } + for ; i+8 <= n; i += 8 { + if swar.Load8(s, i)&swar.HighBits != 0 { + return false + } + } + if i == n { + return true + } + if n >= 8 { + return swar.Load8(s, n-8)&swar.HighBits == 0 + } + for ; i < n; i++ { + if s[i] >= 0x80 { + return false + } + } + return true +} + +// IndexNonQuotable returns the index of the first byte of s that cannot +// appear verbatim inside an RFC 9110 quoted-string — that is, a byte +// matching c == '\\' || c == '"' || (c < 0x20 && c != '\t') || c == 0x7f — +// or -1 if every byte is quotable. qdtext is HTAB / SP / %x21 / %x23-5B / +// %x5D-7E / obs-text, so HTAB and bytes >= 0x80 are quotable. Returning the +// index (rather than a bool) lets callers copy the clean prefix and start +// escaping exactly at the offending byte. Inputs of 8+ bytes finish with +// one overlapping word at n-8, shorter ones byte-wise. +func IndexNonQuotable[S byteSeq](s S) int { + n := len(s) + i := 0 + // Two words per branch: the masks compute independently, and if only the + // second word matched, its first set lane is still exact. + for ; i+16 <= n; i += 16 { + m0 := nonQuotableMask(swar.Load8(s, i)) + m1 := nonQuotableMask(swar.Load8(s, i+8)) + if m0|m1 != 0 { + if m0 != 0 { + return i + swar.FirstLane(m0) + } + return i + 8 + swar.FirstLane(m1) + } + } + for ; i+8 <= n; i += 8 { + if m := nonQuotableMask(swar.Load8(s, i)); m != 0 { + return i + swar.FirstLane(m) + } + } + if i == n { + return -1 + } + if n >= 8 { + if m := nonQuotableMask(swar.Load8(s, n-8)); m != 0 { + return n - 8 + swar.FirstLane(m) + } + return -1 + } + for ; i < n; i++ { + if c := s[i]; c == '\\' || c == '"' || (c < 0x20 && c != '\t') || c == 0x7f { + return i + } + } + return -1 +} + +// nonQuotableMask marks the lanes of w holding bytes that need RFC 9110 +// quoted-string escaping. Like swar.ZeroLanes, the result is exact in and below +// the first marked lane, which is all the first-match scan above consumes. +func nonQuotableMask(w uint64) uint64 { + // Controls (< 0x20) and DEL (0x7F) share one biased range test: + // t := ((c & 0x7F) + 1) & 0x7F maps DEL to 0 and controls to 0x01..0x20, + // so t <= 0x20 captures exactly both; lanes with the high bit set + // (obs-text, always quotable) are excluded by the &^ w term. HTAB is + // quotable qdtext, so its lanes are cleared with an exact match mask — + // an approximate one could wrongly clear a control lane above a tab. + t := ((w & swar.LowSeven) + swar.Ones) & swar.LowSeven + ctl := ^(t + (0x80-0x21)*swar.Ones) &^ w & swar.HighBits &^ swar.MatchByteMask(w, '\t') + return ctl | swar.ZeroLanes(w^quoteLanes) | swar.ZeroLanes(w^backslashLanes) +} diff --git a/src/vendor/github.com/gofiber/utils/v2/bytes/case.go b/src/vendor/github.com/gofiber/utils/v2/bytes/case.go index 483d5b7b..8ad483e9 100644 --- a/src/vendor/github.com/gofiber/utils/v2/bytes/case.go +++ b/src/vendor/github.com/gofiber/utils/v2/bytes/case.go @@ -4,128 +4,102 @@ import ( "github.com/gofiber/utils/v2/internal/caseconv" ) +// swarMinLen is the smallest input length worth routing through the +// word-at-a-time (SWAR) helpers; shorter inputs are cheaper byte-by-byte. +const swarMinLen = caseconv.WordLen + // ToLower converts an ASCII byte slice to lower-case without modifying the input. func ToLower(b []byte) []byte { n := len(b) - if n == 0 { + if n < swarMinLen { + table := caseconv.ToLowerTable + for i := 0; i < n; i++ { + c := b[i] + low := table[c] + if low != c { + dst := make([]byte, n) + copy(dst, b[:i]) + dst[i] = low + for i++; i < n; i++ { + dst[i] = table[b[i]] + } + return dst + } + } return b } - table := caseconv.ToLowerTable - i := 0 - for i < n { - c := b[i] - low := table[c] - if low != c { - dst := make([]byte, n) - copy(dst, b[:i]) - dst[i] = low - i++ - limit := i + ((n - i) &^ 3) - for i < limit { - dst[i+0] = table[b[i+0]] - dst[i+1] = table[b[i+1]] - dst[i+2] = table[b[i+2]] - dst[i+3] = table[b[i+3]] - i += 4 - } - for i < n { - dst[i] = table[b[i]] - i++ - } - return dst - } - i++ + i := caseconv.FirstUpperIndex(b) + if i < 0 { + return b } - return b + + dst := make([]byte, n) + // Copy the unchanged prefix up to the word containing the first + // uppercase byte, then convert the rest word-at-a-time. + from := i &^ (caseconv.WordLen - 1) + copy(dst, b[:from]) + caseconv.ToLowerCopy(dst, b, from) + return dst } // ToUpper converts an ASCII byte slice to upper-case without modifying the input. func ToUpper(b []byte) []byte { n := len(b) - if n == 0 { + if n < swarMinLen { + table := caseconv.ToUpperTable + for i := 0; i < n; i++ { + c := b[i] + up := table[c] + if up != c { + dst := make([]byte, n) + copy(dst, b[:i]) + dst[i] = up + for i++; i < n; i++ { + dst[i] = table[b[i]] + } + return dst + } + } return b } - table := caseconv.ToUpperTable - i := 0 - for i < n { - c := b[i] - up := table[c] - if up != c { - dst := make([]byte, n) - copy(dst, b[:i]) - dst[i] = up - i++ - limit := i + ((n - i) &^ 3) - for i < limit { - dst[i+0] = table[b[i+0]] - dst[i+1] = table[b[i+1]] - dst[i+2] = table[b[i+2]] - dst[i+3] = table[b[i+3]] - i += 4 - } - for i < n { - dst[i] = table[b[i]] - i++ - } - return dst - } - i++ + i := caseconv.FirstLowerIndex(b) + if i < 0 { + return b } - return b + + dst := make([]byte, n) + from := i &^ (caseconv.WordLen - 1) + copy(dst, b[:from]) + caseconv.ToUpperCopy(dst, b, from) + return dst } // UnsafeToLower converts an ASCII byte slice to lower-case in-place. // The passed slice content is modified and the same slice is returned. func UnsafeToLower(b []byte) []byte { - table := caseconv.ToLowerTable - n := len(b) - i := 0 - limit := n &^ 3 - for i < limit { - b0 := b[i+0] - b1 := b[i+1] - b2 := b[i+2] - b3 := b[i+3] - - b[i+0] = table[b0] - b[i+1] = table[b1] - b[i+2] = table[b2] - b[i+3] = table[b3] - - i += 4 - } - for i < n { - b[i] = table[b[i]] - i++ + if len(b) < swarMinLen { + table := caseconv.ToLowerTable + for i := range b { + b[i] = table[b[i]] + } + return b } + caseconv.ToLowerInPlace(b) return b } // UnsafeToUpper converts an ASCII byte slice to upper-case in-place. // The passed slice content is modified and the same slice is returned. func UnsafeToUpper(b []byte) []byte { - table := caseconv.ToUpperTable - n := len(b) - i := 0 - limit := n &^ 3 - for i < limit { - b0 := b[i+0] - b1 := b[i+1] - b2 := b[i+2] - b3 := b[i+3] - - b[i+0] = table[b0] - b[i+1] = table[b1] - b[i+2] = table[b2] - b[i+3] = table[b3] - - i += 4 - } - for i < n { - b[i] = table[b[i]] - i++ + if len(b) < swarMinLen { + table := caseconv.ToUpperTable + for i := range b { + b[i] = table[b[i]] + } + return b } + caseconv.ToUpperInPlace(b) return b } diff --git a/src/vendor/github.com/gofiber/utils/v2/byteseq.go b/src/vendor/github.com/gofiber/utils/v2/byteseq.go index abc9c063..707df64a 100644 --- a/src/vendor/github.com/gofiber/utils/v2/byteseq.go +++ b/src/vendor/github.com/gofiber/utils/v2/byteseq.go @@ -2,6 +2,7 @@ package utils import ( "github.com/gofiber/utils/v2/internal/caseconv" + "github.com/gofiber/utils/v2/swar" ) type byteSeq interface { @@ -10,54 +11,43 @@ type byteSeq interface { // EqualFold tests ascii strings or bytes for equality case-insensitively func EqualFold[S byteSeq](b, s S) bool { - if len(b) != len(s) { + n := len(b) + if n != len(s) { return false } - table := caseconv.ToUpperTable - n := len(b) + // Compare 8 bytes per iteration; case-fold with SWAR only when the raw + // words differ, so byte-identical input skips both folds entirely. i := 0 - - // Unroll by 4 to match other hot paths and drive instruction-level parallelism. - limit := n &^ 3 - for i < limit { - b0 := b[i+0] - s0 := s[i+0] - if table[b0] != table[s0] { + for ; i+8 <= n; i += 8 { + x := swar.Load8(b, i) + y := swar.Load8(s, i) + if x != y && swar.ToUpperWord(x) != swar.ToUpperWord(y) { return false } - - b1 := b[i+1] - s1 := s[i+1] - if table[b1] != table[s1] { - return false - } - - b2 := b[i+2] - s2 := s[i+2] - if table[b2] != table[s2] { - return false - } - - b3 := b[i+3] - s3 := s[i+3] - if table[b3] != table[s3] { - return false - } - - i += 4 + } + if i == n { + return true + } + if n >= 8 { + // Handle the tail with one overlapping word compare; re-checking + // bytes that were already equal cannot change the outcome. + x := swar.Load8(b, n-8) + y := swar.Load8(s, n-8) + return x == y || swar.ToUpperWord(x) == swar.ToUpperWord(y) } - for i < n { + table := caseconv.ToUpperTable + for ; i < n; i++ { if table[b[i]] != table[s[i]] { return false } - i++ } return true } -// TrimLeft is the equivalent of strings/bytes.TrimLeft +// TrimLeft removes all leading occurrences of the byte cutset from s. +// Unlike strings/bytes.TrimLeft, cutset is a single byte, not a set of characters. func TrimLeft[S byteSeq](s S, cutset byte) S { lenStr, start := len(s), 0 for start < lenStr && s[start] == cutset { @@ -66,7 +56,8 @@ func TrimLeft[S byteSeq](s S, cutset byte) S { return s[start:] } -// Trim is the equivalent of strings/bytes.Trim +// Trim removes all leading and trailing occurrences of the byte cutset from s. +// Unlike strings/bytes.Trim, cutset is a single byte, not a set of characters. func Trim[S byteSeq](s S, cutset byte) S { i, j := 0, len(s)-1 for ; i <= j; i++ { @@ -83,7 +74,8 @@ func Trim[S byteSeq](s S, cutset byte) S { return s[i : j+1] } -// TrimRight is the equivalent of strings/bytes.TrimRight +// TrimRight removes all trailing occurrences of the byte cutset from s. +// Unlike strings/bytes.TrimRight, cutset is a single byte, not a set of characters. func TrimRight[S byteSeq](s S, cutset byte) S { lenStr := len(s) for lenStr > 0 && s[lenStr-1] == cutset { diff --git a/src/vendor/github.com/gofiber/utils/v2/common.go b/src/vendor/github.com/gofiber/utils/v2/common.go index 8618f510..bf266808 100644 --- a/src/vendor/github.com/gofiber/utils/v2/common.go +++ b/src/vendor/github.com/gofiber/utils/v2/common.go @@ -127,7 +127,10 @@ func IncrementIPRange(ip net.IP) { } // ConvertToBytes returns integer size of bytes from human-readable string, ex. 42kb, 42M -// Returns 0 if string is unrecognized +// Decimal units are powers of 1000 (42k = 42000); binary units with an 'i' infix +// are powers of 1024 (42Ki = 43008). Note that ByteSize formats with powers of 1024, +// so use binary suffixes for a lossless round-trip. +// Returns 0 if the string is unrecognized or negative. func ConvertToBytes(humanReadableString string) int { strLen := len(humanReadableString) if strLen == 0 { @@ -189,7 +192,7 @@ func ConvertToBytes(humanReadableString string) int { if strings.IndexByte(numPart, '.') >= 0 { var err error size, err = ParseFloat64(numPart) - if err != nil { + if err != nil || size < 0 { return 0 } } else { @@ -201,17 +204,28 @@ func ConvertToBytes(humanReadableString string) int { } if unitPrefixPos > 0 { + // An 'i' after the unit prefix selects binary units (KiB = 1024). + binary := unitPrefixPos+1 < strLen && + (humanReadableString[unitPrefixPos+1] == 'i' || humanReadableString[unitPrefixPos+1] == 'I') + var decMul, binMul float64 switch humanReadableString[unitPrefixPos] { case 'k', 'K': - size *= 1e3 + decMul, binMul = 1e3, 1<<10 case 'm', 'M': - size *= 1e6 + decMul, binMul = 1e6, 1<<20 case 'g', 'G': - size *= 1e9 + decMul, binMul = 1e9, 1<<30 case 't', 'T': - size *= 1e12 + decMul, binMul = 1e12, 1<<40 case 'p', 'P': - size *= 1e15 + decMul, binMul = 1e15, 1<<50 + default: + decMul, binMul = 1, 1 + } + if binary { + size *= binMul + } else { + size *= decMul } } diff --git a/src/vendor/github.com/gofiber/utils/v2/convert.go b/src/vendor/github.com/gofiber/utils/v2/convert.go index 21422e5e..bd2f346c 100644 --- a/src/vendor/github.com/gofiber/utils/v2/convert.go +++ b/src/vendor/github.com/gofiber/utils/v2/convert.go @@ -150,6 +150,9 @@ func ToString(arg any, timeFormat ...string) string { return "" } return ToString(v.Interface(), timeFormat...) + // error before fmt.Stringer to match the fmt package's precedence + case error: + return v.Error() case fmt.Stringer: return v.String() // Handle common pointer types directly to avoid reflection @@ -188,32 +191,42 @@ func ToString(arg any, timeFormat ...string) string { if len(v) == 0 { return "[]" } - var buf strings.Builder - buf.Grow(len(v) * 8) // Pre-allocate approximate size - buf.WriteByte('[') + // Compute the exact output size for a single allocation: + // '[' + ']' + one separating space per gap + all element bytes. + size := len(v) + 1 + for _, s := range v { + size += len(s) + } + buf := make([]byte, 0, size) + buf = append(buf, '[') for i, s := range v { if i > 0 { - buf.WriteByte(' ') + buf = append(buf, ' ') } - buf.WriteString(s) + buf = append(buf, s...) } - buf.WriteByte(']') - return buf.String() + buf = append(buf, ']') + return UnsafeString(buf) case []int: if len(v) == 0 { return "[]" } - var buf strings.Builder - buf.Grow(len(v) * 4) // Pre-allocate approximate size - buf.WriteByte('[') + // Compute the exact output size for a single allocation: + // '[' + ']' + one separating space per gap + all element digits. + size := len(v) + 1 + for _, n := range v { + size += intDigits(int64(n)) + } + buf := make([]byte, 0, size) + buf = append(buf, '[') for i, n := range v { if i > 0 { - buf.WriteByte(' ') + buf = append(buf, ' ') } - buf.WriteString(FormatInt(int64(n))) + buf = AppendInt(buf, int64(n)) } - buf.WriteByte(']') - return buf.String() + buf = append(buf, ']') + return UnsafeString(buf) default: // Check if the type is a pointer by using reflection rv := reflect.ValueOf(arg) diff --git a/src/vendor/github.com/gofiber/utils/v2/format.go b/src/vendor/github.com/gofiber/utils/v2/format.go index ad85b007..6c81d14a 100644 --- a/src/vendor/github.com/gofiber/utils/v2/format.go +++ b/src/vendor/github.com/gofiber/utils/v2/format.go @@ -1,5 +1,9 @@ package utils +import ( + "math/bits" +) + // smallInts contains precomputed string representations for small integers 0-99 var smallInts [100]string @@ -189,6 +193,37 @@ func FormatInt8(n int8) string { return int8Strs[uint8(n)] } +// pow10 holds the powers of ten representable in a uint64. +var pow10 = [20]uint64{ + 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, +} + +// uintDigits returns the number of decimal digits in n. It is loop-free so it +// inlines into per-element sizing passes. +func uintDigits(n uint64) int { + if n < 10 { + return 1 + } + // floor(log10(n)) approximated from floor(log2(n)): 1233/4096 ~= log10(2). + t := (bits.Len64(n) * 1233) >> 12 + if n < pow10[t] { + return t + } + return t + 1 +} + +// intDigits returns the length of the decimal string representation of n, +// including the '-' sign for negative values. +func intDigits(n int64) int { + if n < 0 { + // uint64(-n) yields the absolute value for all negatives via two's + // complement, including math.MinInt64. + return 1 + uintDigits(uint64(-n)) + } + return uintDigits(uint64(n)) +} + // AppendUint appends the decimal string representation of n to dst. func AppendUint(dst []byte, n uint64) []byte { if n < 100 { diff --git a/src/vendor/github.com/gofiber/utils/v2/http.go b/src/vendor/github.com/gofiber/utils/v2/http.go index 8baaf3e6..99e762bd 100644 --- a/src/vendor/github.com/gofiber/utils/v2/http.go +++ b/src/vendor/github.com/gofiber/utils/v2/http.go @@ -36,8 +36,9 @@ func GetMIME(extension string) string { extWithDot = "." + extension } - // Single map lookup with normalized key - if foundMime := mimeExtensions[extWithoutDot]; len(foundMime) > 0 { + // Single map lookup with normalized key. Extensions are matched + // case-insensitively; ToLower only allocates for upper-case input. + if foundMime := mimeExtensions[casestrings.ToLower(extWithoutDot)]; len(foundMime) > 0 { return foundMime } diff --git a/src/vendor/github.com/gofiber/utils/v2/internal/caseconv/swar.go b/src/vendor/github.com/gofiber/utils/v2/internal/caseconv/swar.go new file mode 100644 index 00000000..5bbf3a84 --- /dev/null +++ b/src/vendor/github.com/gofiber/utils/v2/internal/caseconv/swar.go @@ -0,0 +1,246 @@ +package caseconv + +import ( + "encoding/binary" + + "github.com/gofiber/utils/v2/swar" +) + +// Word-at-a-time ASCII case conversion built on the public SWAR primitives +// in github.com/gofiber/utils/v2/swar. Byte order inside a word does not +// matter for the case folds themselves (every operation is per byte lane), +// so these helpers are correct on both little- and big-endian platforms. +// +// Words move through encoding/binary rather than swar.Load8/swar.Store8 on +// purpose: the binary.LittleEndian forms measure ~9% faster in the in-place +// word loops here (benchstat -count=10, Go 1.25, Apple M2 Pro). The lane +// order is identical, so swar words and binary.LittleEndian loads/stores +// can be mixed freely. + +// WordLen is the SWAR word width in bytes, re-exported for the case +// conversion callers in the strings and bytes packages: they route inputs +// shorter than WordLen to byte-wise paths and align offsets they pass (such +// as ToLowerCopy's from) with WordLen-derived masks. +const WordLen = swar.WordLen + +// FirstUpperIndex returns the index of the first ASCII uppercase byte in b, +// or -1 if b contains none. b is only ever read; it may be an unsafe view +// over immutable string memory. +func FirstUpperIndex(b []byte) int { + n := len(b) + i := 0 + if n >= 32 { + // Check the first word alone: canonical mixed-case values + // ("Content-Type") change case right at the start, and that path + // must stay one mask. + if m := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[0:8]), 'A', 'Z'); m != 0 { + return swar.FirstLane(m) + } + // Then four words per branch: the masks compute independently, so + // the common no-uppercase case pays one test per 32 bytes. The + // MatchRangeMask computations stay inline so their constant ranges + // fold; only the cold hit-resolution ladder is shared, and it + // inlines (the no-match loop never reaches it). + for i = 8; i+32 <= n; i += 32 { + m0 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i:i+8]), 'A', 'Z') + m1 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+8:i+16]), 'A', 'Z') + m2 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+16:i+24]), 'A', 'Z') + m3 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+24:i+32]), 'A', 'Z') + if m0|m1|m2|m3 != 0 { + return firstSetLane32(i, m0, m1, m2, m3) + } + } + if i == n { + return -1 + } + // Finish with one overlapping block at n-32. MatchRangeMask is + // per-lane exact, so re-scanned lanes are known non-matching and the + // first set lane always falls in the new bytes. + i = n - 32 + m0 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i:i+8]), 'A', 'Z') + m1 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+8:i+16]), 'A', 'Z') + m2 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+16:i+24]), 'A', 'Z') + m3 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+24:i+32]), 'A', 'Z') + if m0|m1|m2|m3 != 0 { + return firstSetLane32(i, m0, m1, m2, m3) + } + return -1 + } + for ; i+8 <= n; i += 8 { + if m := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i:i+8]), 'A', 'Z'); m != 0 { + return i + swar.FirstLane(m) + } + } + if i == n { + return -1 + } + if n >= 8 { + // Scan the tail with one overlapping word; lanes that were already + // scanned are known to be zero, so FirstLane lands in the new bytes. + if m := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[n-8:n]), 'A', 'Z'); m != 0 { + return n - 8 + swar.FirstLane(m) + } + return -1 + } + for ; i < n; i++ { + if c := b[i]; c >= 'A' && c <= 'Z' { + return i + } + } + return -1 +} + +// FirstLowerIndex returns the index of the first ASCII lowercase byte in b, +// or -1 if b contains none. b is only ever read; it may be an unsafe view +// over immutable string memory. +// firstSetLane32 resolves a hit inside a 32-byte block: given the block's +// four per-word masks, it returns the index of the first marked byte +// relative to base, the block's starting offset. It runs at most once per +// scan — only after a block's combined mask tested non-zero — and is small +// enough to inline, so the hot no-match loops pay nothing for the sharing. +func firstSetLane32(base int, m0, m1, m2, m3 uint64) int { + if m0 != 0 { + return base + swar.FirstLane(m0) + } + if m1 != 0 { + return base + 8 + swar.FirstLane(m1) + } + if m2 != 0 { + return base + 16 + swar.FirstLane(m2) + } + return base + 24 + swar.FirstLane(m3) +} + +func FirstLowerIndex(b []byte) int { + n := len(b) + i := 0 + if n >= 32 { + // Structure and rationale mirror FirstUpperIndex. + if m := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[0:8]), 'a', 'z'); m != 0 { + return swar.FirstLane(m) + } + for i = 8; i+32 <= n; i += 32 { + m0 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i:i+8]), 'a', 'z') + m1 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+8:i+16]), 'a', 'z') + m2 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+16:i+24]), 'a', 'z') + m3 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+24:i+32]), 'a', 'z') + if m0|m1|m2|m3 != 0 { + return firstSetLane32(i, m0, m1, m2, m3) + } + } + if i == n { + return -1 + } + i = n - 32 + m0 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i:i+8]), 'a', 'z') + m1 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+8:i+16]), 'a', 'z') + m2 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+16:i+24]), 'a', 'z') + m3 := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i+24:i+32]), 'a', 'z') + if m0|m1|m2|m3 != 0 { + return firstSetLane32(i, m0, m1, m2, m3) + } + return -1 + } + for ; i+8 <= n; i += 8 { + if m := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[i:i+8]), 'a', 'z'); m != 0 { + return i + swar.FirstLane(m) + } + } + if i == n { + return -1 + } + if n >= 8 { + if m := swar.MatchRangeMask(binary.LittleEndian.Uint64(b[n-8:n]), 'a', 'z'); m != 0 { + return n - 8 + swar.FirstLane(m) + } + return -1 + } + for ; i < n; i++ { + if c := b[i]; c >= 'a' && c <= 'z' { + return i + } + } + return -1 +} + +// ToLowerInPlace lower-cases every ASCII uppercase byte of b in place. +func ToLowerInPlace(b []byte) { + n := len(b) + i := 0 + for ; i+8 <= n; i += 8 { + binary.LittleEndian.PutUint64(b[i:i+8], swar.ToLowerWord(binary.LittleEndian.Uint64(b[i:i+8]))) + } + // Finish byte-wise: an overlapping word here would partially overlap the + // previous 8-byte store and stall on store-to-load forwarding. + for ; i < n; i++ { + b[i] = ToLowerTable[b[i]] + } +} + +// ToUpperInPlace upper-cases every ASCII lowercase byte of b in place. +func ToUpperInPlace(b []byte) { + n := len(b) + i := 0 + for ; i+8 <= n; i += 8 { + binary.LittleEndian.PutUint64(b[i:i+8], swar.ToUpperWord(binary.LittleEndian.Uint64(b[i:i+8]))) + } + for ; i < n; i++ { + b[i] = ToUpperTable[b[i]] + } +} + +// ToLowerCopy writes the lower-cased content of src into dst starting at +// byte offset from. Bytes before from must already be present in dst, and +// len(dst) must equal len(src). src is only ever read — the strings package +// passes an unsafe view over a string's immutable backing memory — so no +// code path here may write through src. +// +// from must be a multiple of WordLen and at or below the index of the first +// byte that lower-casing changes: when len(src) >= WordLen the overlapping +// tail store rewrites dst[n-WordLen:from) with case-converted bytes, which is +// only a no-op under that precondition. +// +// Callers currently invoke this only with len(src) >= WordLen; the byte-wise +// tail below keeps the helper correct standalone for shorter inputs. +func ToLowerCopy(dst, src []byte, from int) { + n := len(src) + i := from + for ; i+8 <= n; i += 8 { + binary.LittleEndian.PutUint64(dst[i:i+8], swar.ToLowerWord(binary.LittleEndian.Uint64(src[i:i+8]))) + } + if i == n { + return + } + if n >= 8 { + binary.LittleEndian.PutUint64(dst[n-8:n], swar.ToLowerWord(binary.LittleEndian.Uint64(src[n-8:n]))) + return + } + for ; i < n; i++ { + dst[i] = ToLowerTable[src[i]] + } +} + +// ToUpperCopy writes the upper-cased content of src into dst starting at +// byte offset from. Bytes before from must already be present in dst, and +// len(dst) must equal len(src). +// +// from must be a multiple of WordLen and at or below the index of the first +// byte that upper-casing changes; see ToLowerCopy for why, including the +// note on the byte-wise tail and the requirement that src is never written. +func ToUpperCopy(dst, src []byte, from int) { + n := len(src) + i := from + for ; i+8 <= n; i += 8 { + binary.LittleEndian.PutUint64(dst[i:i+8], swar.ToUpperWord(binary.LittleEndian.Uint64(src[i:i+8]))) + } + if i == n { + return + } + if n >= 8 { + binary.LittleEndian.PutUint64(dst[n-8:n], swar.ToUpperWord(binary.LittleEndian.Uint64(src[n-8:n]))) + return + } + for ; i < n; i++ { + dst[i] = ToUpperTable[src[i]] + } +} diff --git a/src/vendor/github.com/gofiber/utils/v2/parse.go b/src/vendor/github.com/gofiber/utils/v2/parse.go index e3f5e1d7..2df8ae7c 100644 --- a/src/vendor/github.com/gofiber/utils/v2/parse.go +++ b/src/vendor/github.com/gofiber/utils/v2/parse.go @@ -3,10 +3,11 @@ package utils import ( "math" "strconv" + + "github.com/gofiber/utils/v2/swar" ) const ( - maxFracDigits = 16 maxUint64Cutoff = math.MaxUint64 / 10 maxUint64Cutlim = math.MaxUint64 % 10 ) @@ -34,8 +35,24 @@ func ParseNativeUint[S byteSeq](s S) (uint, error) { // Returns the parsed value and nil on success, else 0 and an error. func ParseInt[S byteSeq](s S) (int64, error) { if len(s) > 0 && s[0] != '-' && s[0] != '+' && len(s) <= 19 { + // At most 19 digits fit here, so two 8-digit SWAR steps plus a + // scalar remainder can never overflow before the final range check. + // A word with a non-digit lane falls through to the scalar loop, + // which reports the syntax error. The body deliberately duplicates + // the parseDigitsBig structure: routing through parseDigitsBig + // instead costs +22% on the 9-digit shape (benchstat -count=10, + // Go 1.25, Apple M2 Pro), mostly digit bookkeeping and the extra + // call frame. var n uint64 - for i := range len(s) { + i := 0 + for ; len(s)-i >= 8; i += 8 { + w := swar.Load8(s, i) + if !isEightDigits(w) { + break + } + n = n*100000000 + parse8Digits(w) + } + for ; i < len(s); i++ { c := s[i] - '0' if c > 9 { return 0, &strconv.NumError{Func: "ParseInt", Num: string(s), Err: strconv.ErrSyntax} @@ -146,11 +163,67 @@ func ParseUint8[S byteSeq](s S) (uint8, error) { return parseUnsigned[S, uint8]("ParseUint8", s, uint8(math.MaxUint8)) } -// parseDigits parses a sequence of digits and returns the uint64 value. -// It returns an error if any non-digit is encountered or overflow happens. +// isEightDigits reports whether every lane of w is an ASCII digit ('0'..'9'). +// High nibbles must all be 3 and low nibbles must not carry past 9 when 6 is +// added. A lane >= 0xFA can carry into its neighbor's sum, but such a lane +// already fails its own high-nibble test, so the answer stays exact. +func isEightDigits(w uint64) bool { + const ( + highNibbles = 0xF0F0F0F0F0F0F0F0 + sixes = 0x0606060606060606 + threes = 0x3333333333333333 + ) + return (w&highNibbles)|((w+sixes)&highNibbles)>>4 == threes +} + +// parse8Digits converts a word of 8 ASCII digit bytes (first digit in lane 0, +// most significant) to its numeric value using two pairwise multiply-combine +// steps. The caller must have validated that every lane is '0'..'9'. +func parse8Digits(w uint64) uint64 { + const ( + digitZeros = 0x3030303030303030 // '0' in every lane + pairMask = 0x000000FF000000FF + mul1 = 100 + (1000000 << 32) + mul2 = 1 + (10000 << 32) + ) + w -= digitZeros + w = w*10 + w>>8 // adjacent digit pairs -> 2-digit values in even lanes + return ((w&pairMask)*mul1 + (w>>16&pairMask)*mul2) >> 32 +} + +// parseDigits parses a run of fewer than 8 digits and returns the uint64 +// value, or an error on the first non-digit. Callers must route runs of 8+ +// bytes to parseDigitsBig — that is what makes overflow impossible here (at +// most 7 digits) and keeps this body under the inlining budget. func parseDigits[S byteSeq](s S, i int) (uint64, error) { + var n uint64 + for ; i < len(s); i++ { + c := s[i] - '0' + if c > 9 { + return 0, strconv.ErrSyntax + } + n = n*10 + uint64(c) + } + return n, nil +} + +// parseDigitsBig parses digit runs of 8+ bytes, consuming 8 digits per word +// while the running value is guaranteed to still fit: two full steps reach 16 +// digits, and any value with <= 19 digits fits in uint64. A word with any +// non-digit lane falls through to the scalar loop, which reports syntax +// errors byte-precisely and applies the overflow checks. +func parseDigitsBig[S byteSeq](s S, i int) (uint64, error) { var n uint64 digits := 0 + for len(s)-i >= 8 && digits+8 <= 19 { + w := swar.Load8(s, i) + if !isEightDigits(w) { + break + } + n = n*100000000 + parse8Digits(w) + digits += 8 + i += 8 + } for ; i < len(s); i++ { c := s[i] - '0' if c > 9 { @@ -187,8 +260,14 @@ func parseSigned[S byteSeq, T Signed](fn string, s S, minRange, maxRange T) (T, return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrSyntax} } - // Parse digits. - n, err := parseDigits(s, i) + // Parse digits, taking the 8-digits-per-word path for long runs. + var n uint64 + var err error + if len(s)-i >= 8 { + n, err = parseDigitsBig(s, i) + } else { + n, err = parseDigits(s, i) + } if err != nil { return 0, &strconv.NumError{Func: fn, Num: string(s), Err: err} } @@ -217,8 +296,15 @@ func parseUnsigned[S byteSeq, T Unsigned](fn string, s S, maxRange T) (T, error) return 0, &strconv.NumError{Func: fn, Num: "", Err: strconv.ErrSyntax} } - // Parse digits directly from index 0. - n, err := parseDigits(s, 0) + // Parse digits directly from index 0, taking the 8-digits-per-word path + // for long runs. + var n uint64 + var err error + if len(s) >= 8 { + n, err = parseDigitsBig(s, 0) + } else { + n, err = parseDigits(s, 0) + } if err != nil { return 0, &strconv.NumError{Func: fn, Num: string(s), Err: err} } @@ -249,22 +335,26 @@ func parseFloat[S byteSeq](fn string, s S) (float64, error) { return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrSyntax} } - var intPart uint64 + // Collect integer and fractional digits into a single mantissa and track + // the decimal exponent. Digits beyond uint64 precision are dropped: integer + // digits shift the exponent up, fractional digits are simply ignored. + var mantissa uint64 + var exp10 int + digits := 0 for i < len(s) { c := s[i] - '0' if c > 9 { break } - if intPart > maxUint64Cutoff || (intPart == maxUint64Cutoff && uint64(c) > maxUint64Cutlim) { - return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrRange} + if mantissa > maxUint64Cutoff || (mantissa == maxUint64Cutoff && uint64(c) > maxUint64Cutlim) { + exp10++ + } else { + mantissa = mantissa*10 + uint64(c) } - intPart = intPart*10 + uint64(c) + digits++ i++ } - var fracPart uint64 - var fracDiv uint64 = 1 - var fracDigits int if i < len(s) && s[i] == '.' { i++ for i < len(s) { @@ -272,16 +362,19 @@ func parseFloat[S byteSeq](fn string, s S) (float64, error) { if c > 9 { break } - if fracDigits >= maxFracDigits { - return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrRange} + if mantissa < maxUint64Cutoff || (mantissa == maxUint64Cutoff && uint64(c) <= maxUint64Cutlim) { + mantissa = mantissa*10 + uint64(c) + exp10-- } - fracPart = fracPart*10 + uint64(c) - fracDiv *= 10 - fracDigits++ + digits++ i++ } } + if digits == 0 { + return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrSyntax} + } + var expSign bool var exp int64 if i < len(s) && (s[i] == 'e' || s[i] == 'E') { @@ -299,17 +392,17 @@ func parseFloat[S byteSeq](fn string, s S) (float64, error) { if i == len(s) { return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrSyntax} } + // Saturate the parsed exponent far beyond anything exp10 (bounded by + // the input length) could pull back into range. Clamping to the final + // range must wait until both are combined below. + const maxParsedExp = int64(1) << 50 for i < len(s) { c := s[i] - '0' if c > 9 { return 0, &strconv.NumError{Func: fn, Num: string(s), Err: strconv.ErrSyntax} } - exp = exp*10 + int64(c) - if !expSign && exp > 308 { - exp = 309 - } - if expSign && exp > 324 { - exp = 325 + if exp < maxParsedExp { + exp = exp*10 + int64(c) } i++ } @@ -321,12 +414,21 @@ func parseFloat[S byteSeq](fn string, s S) (float64, error) { if expSign { exp = -exp } - - f := float64(intPart) - if fracPart > 0 { - f += float64(fracPart) / float64(fracDiv) + // Clamp the combined exponent to math.Pow10's saturation range so the + // int conversion below cannot wrap on 32-bit platforms for extremely + // long digit strings; larger magnitudes overflow (Inf, caught below) or + // underflow (Pow10 returns 0) anyway. + exp += int64(exp10) + if exp > 309 { + exp = 309 + } else if exp < -325 { + exp = -325 } - if exp != 0 { + + f := float64(mantissa) + // Skip scaling for a zero mantissa: 0 * Pow10(309) would be 0 * Inf = NaN + // and turn inputs like "0e400" into a spurious range error. + if exp != 0 && f != 0 { f *= math.Pow10(int(exp)) } if neg { diff --git a/src/vendor/github.com/gofiber/utils/v2/search.go b/src/vendor/github.com/gofiber/utils/v2/search.go new file mode 100644 index 00000000..58df1e45 --- /dev/null +++ b/src/vendor/github.com/gofiber/utils/v2/search.go @@ -0,0 +1,321 @@ +package utils + +import ( + "github.com/gofiber/utils/v2/internal/caseconv" + "github.com/gofiber/utils/v2/swar" +) + +// Multi-needle and case-insensitive searching, built on swar.ZeroLanes +// first-match masks with needle broadcasts hoisted out of the word loops. +// IndexAny2/IndexAny3 scan words while i+8 <= n and finish 8+ byte inputs +// with one overlapping word at n-8 (pure loads; already-scanned lanes are +// known non-matching, so the first set lane falls in the new bytes). +// IndexFold instead scans candidate start positions only up to n-k, so its +// word loop hands the final partial word to a scalar remainder; the +// overlapping-window trick appears there only inside the verify reads. +// Inputs shorter than a word use scalar loops throughout. + +// IndexAny2 returns the index of the first occurrence in s of either a or b, +// or -1 if neither is present. +func IndexAny2[S byteSeq](s S, a, b byte) int { + n := len(s) + if n >= 8 { + // The needle broadcasts are hoisted here so sub-word inputs never + // pay for the multiplies. + bcA := swar.Broadcast(a) + bcB := swar.Broadcast(b) + i := 0 + // Two words per branch: the masks compute independently, and if only + // the second word matched, its first set lane is still exact. + for ; i+16 <= n; i += 16 { + w0 := swar.Load8(s, i) + w1 := swar.Load8(s, i+8) + m0 := swar.ZeroLanes(w0^bcA) | swar.ZeroLanes(w0^bcB) + m1 := swar.ZeroLanes(w1^bcA) | swar.ZeroLanes(w1^bcB) + if m0|m1 != 0 { + if m0 != 0 { + return i + swar.FirstLane(m0) + } + return i + 8 + swar.FirstLane(m1) + } + } + for ; i+8 <= n; i += 8 { + w := swar.Load8(s, i) + if m := swar.ZeroLanes(w^bcA) | swar.ZeroLanes(w^bcB); m != 0 { + return i + swar.FirstLane(m) + } + } + if i == n { + return -1 + } + w := swar.Load8(s, n-8) + if m := swar.ZeroLanes(w^bcA) | swar.ZeroLanes(w^bcB); m != 0 { + return n - 8 + swar.FirstLane(m) + } + return -1 + } + for i := range n { + if s[i] == a || s[i] == b { + return i + } + } + return -1 +} + +// IndexAny3 returns the index of the first occurrence in s of a, b, or c, +// or -1 if none is present. +func IndexAny3[S byteSeq](s S, a, b, c byte) int { + n := len(s) + if n >= 8 { + bcA := swar.Broadcast(a) + bcB := swar.Broadcast(b) + bcC := swar.Broadcast(c) + i := 0 + // Two words per branch; see IndexAny2. + for ; i+16 <= n; i += 16 { + w0 := swar.Load8(s, i) + w1 := swar.Load8(s, i+8) + m0 := swar.ZeroLanes(w0^bcA) | swar.ZeroLanes(w0^bcB) | swar.ZeroLanes(w0^bcC) + m1 := swar.ZeroLanes(w1^bcA) | swar.ZeroLanes(w1^bcB) | swar.ZeroLanes(w1^bcC) + if m0|m1 != 0 { + if m0 != 0 { + return i + swar.FirstLane(m0) + } + return i + 8 + swar.FirstLane(m1) + } + } + for ; i+8 <= n; i += 8 { + w := swar.Load8(s, i) + if m := swar.ZeroLanes(w^bcA) | swar.ZeroLanes(w^bcB) | swar.ZeroLanes(w^bcC); m != 0 { + return i + swar.FirstLane(m) + } + } + if i == n { + return -1 + } + w := swar.Load8(s, n-8) + if m := swar.ZeroLanes(w^bcA) | swar.ZeroLanes(w^bcB) | swar.ZeroLanes(w^bcC); m != 0 { + return n - 8 + swar.FirstLane(m) + } + return -1 + } + for i := range n { + if s[i] == a || s[i] == b || s[i] == c { + return i + } + } + return -1 +} + +// IndexFold returns the index of the first ASCII case-insensitive occurrence +// of needle in s, or -1 if absent. An empty needle matches at index 0. Only +// 'A'..'Z'/'a'..'z' fold; every other byte (including >= 0x80) must match +// exactly, so e.g. "no\rcache" does NOT match the needle "no-cache". +// The needle is a plain string by design: call sites pass constant tokens, +// and a []byte needle would cost its callers a conversion either way. +func IndexFold[S byteSeq](s S, needle string) int { + n, k := len(s), len(needle) + if k == 0 { + return 0 + } + if k > n { + return -1 + } + + table := caseconv.ToLowerTable + first := table[needle[0]] + last := n - k // last valid start position + + if n >= 8 { + // Scan candidate positions 8 at a time by matching the needle's + // first byte in both case variants (no need to fold the haystack), + // then verify each candidate. Approximate-mask false positives just + // cost a verify; the candidate order stays low-to-high, so the + // pos > last cutoff remains a valid global exit. + bc1 := swar.Broadcast(first) + bc2 := bc1 + if first >= 'a' && first <= 'z' { + bc2 = swar.Broadcast(first - 0x20) + } + + // For needles up to one word the verify is a single masked word + // compare against the folded needle, built lazily on the first + // candidate; longer needles fold-compare word-at-a-time. The lazy + // build is load-bearing: building eagerly costs the no-candidate + // path +44% on 8-byte misses and +13% to +17% on 32-64B misses + // (benchstat -count=10, Go 1.25, Apple M2 Pro). foldPrep keeps the + // pair computation in one place for both build sites below. + var fn foldedNeedle + built := false + + i := 0 + for ; i+8 <= n; i += 8 { + w := swar.Load8(s, i) + cand := swar.ZeroLanes(w^bc1) | swar.ZeroLanes(w^bc2) + for cand != 0 { + pos := i + swar.FirstLane(cand) + if pos > last { + return -1 + } + if k <= 8 { + if !built { + fn = foldPrep(needle) + built = true + } + if foldedWindowAt(s, pos, fn.mask) == fn.word { + return pos + } + } else if foldEqualAt(s, pos, needle) { + return pos + } + cand &= cand - 1 + } + } + // Candidate starts in the final partial word: positions [i, last]. + // Here i is the word loop's exit index n - n%8, so this region is + // non-empty only when k <= n%8 <= 7 — the needle always fits in one + // word and only the short-needle verify can apply. + for ; i <= last; i++ { + if table[s[i]] == first { + if !built { + fn = foldPrep(needle) + built = true + } + if foldedWindowAt(s, i, fn.mask) == fn.word { + return i + } + } + } + return -1 + } + + // Scalar path: s shorter than a word (so k <= n < 8). +outer: + for i := 0; i <= last; i++ { + if table[s[i]] != first { + continue + } + for j := 1; j < k; j++ { + if table[s[i+j]] != table[needle[j]] { + continue outer + } + } + return i + } + return -1 +} + +// foldedNeedle is the short-needle (k <= 8) verify state: the needle +// lower-cased into one word plus the mask covering its lanes. +type foldedNeedle struct { + word, mask uint64 +} + +// foldPrep builds the foldedNeedle pair; it exists so IndexFold's two lazy +// build sites share one definition. +func foldPrep(needle string) foldedNeedle { + return foldedNeedle{foldNeedle(needle), ^uint64(0) >> ((8 - len(needle)) * 8)} +} + +// foldNeedle returns needle lower-cased into one little-endian word, lane 0 +// holding needle[0]. len(needle) must be 1..8; lanes past the needle are +// zero, matching the ^uint64(0) >> ((8-k)*8) mask callers apply to windows. +func foldNeedle(needle string) uint64 { + k := len(needle) + if k == 8 { + return swar.ToLowerWord(swar.Load8(needle, 0)) + } + table := caseconv.ToLowerTable + var word uint64 + for j := k - 1; j >= 0; j-- { + word = word<<8 | uint64(table[needle[j]]) + } + return word +} + +// foldedWindowAt returns the lower-cased window of s starting at pos, masked +// to the needle length. Requires len(s) >= 8 and the window to fit in s; +// windows running past len(s)-8 are read through one overlapping load at +// len(s)-8 and shifted so lane 0 is s[pos]. +func foldedWindowAt[S byteSeq](s S, pos int, lenMask uint64) uint64 { + if pos+8 <= len(s) { + return swar.ToLowerWord(swar.Load8(s, pos)) & lenMask + } + from := len(s) - 8 + w := swar.ToLowerWord(swar.Load8(s, from)) >> (8 * (pos - from)) + return w & lenMask +} + +// foldEqualAt reports whether s[pos:pos+len(needle)] equals needle ASCII +// case-insensitively. len(needle) must be >= 8 and pos+len(needle) must be +// within s; the tail is one overlapping word compare. +func foldEqualAt[S byteSeq](s S, pos int, needle string) bool { + k := len(needle) + i := 0 + for ; i+8 <= k; i += 8 { + if swar.ToLowerWord(swar.Load8(s, pos+i)) != swar.ToLowerWord(swar.Load8(needle, i)) { + return false + } + } + if i == k { + return true + } + return swar.ToLowerWord(swar.Load8(s, pos+k-8)) == swar.ToLowerWord(swar.Load8(needle, k-8)) +} + +// ContainsFold reports whether s contains needle, ASCII case-insensitively. +func ContainsFold[S byteSeq](s S, needle string) bool { + return IndexFold(s, needle) >= 0 +} + +// HasPrefixFold reports whether s begins with prefix, ASCII +// case-insensitively: only 'A'..'Z'/'a'..'z' fold, every other byte +// (including >= 0x80) must match exactly, mirroring IndexFold. An empty +// prefix matches any s. Like the other Fold helpers the needle is a plain +// string, since call sites pass constant tokens. +func HasPrefixFold[S byteSeq](s S, prefix string) bool { + k := len(prefix) + if len(s) < k { + return false + } + table := caseconv.ToLowerTable + if k >= 8 { + // First-byte pre-check: typical misses differ immediately, and two + // table loads reject them without the word-compare setup. + if table[s[0]] != table[prefix[0]] { + return false + } + return foldEqualAt(s, 0, prefix) + } + for j := range k { + if table[s[j]] != table[prefix[j]] { + return false + } + } + return true +} + +// HasSuffixFold reports whether s ends with suffix, ASCII +// case-insensitively, under the same folding contract as HasPrefixFold. +// An empty suffix matches any s. +func HasSuffixFold[S byteSeq](s S, suffix string) bool { + k := len(suffix) + n := len(s) + if n < k { + return false + } + table := caseconv.ToLowerTable + if k >= 8 { + // Pre-check the last byte; see HasPrefixFold. + if table[s[n-1]] != table[suffix[k-1]] { + return false + } + return foldEqualAt(s, n-k, suffix) + } + for j := range k { + if table[s[n-k+j]] != table[suffix[j]] { + return false + } + } + return true +} diff --git a/src/vendor/github.com/gofiber/utils/v2/strings/case.go b/src/vendor/github.com/gofiber/utils/v2/strings/case.go index 59860553..c50654cb 100644 --- a/src/vendor/github.com/gofiber/utils/v2/strings/case.go +++ b/src/vendor/github.com/gofiber/utils/v2/strings/case.go @@ -5,66 +5,78 @@ import ( "github.com/gofiber/utils/v2/internal/unsafeconv" ) +// swarMinLen is the smallest input length worth routing through the +// word-at-a-time (SWAR) helpers; shorter inputs are cheaper byte-by-byte. +const swarMinLen = caseconv.WordLen + // ToLower converts an ASCII string to lower-case without modifying the input. func ToLower(s string) string { n := len(s) - if n == 0 { + if n < swarMinLen { + table := caseconv.ToLowerTable + for i := 0; i < n; i++ { + c := s[i] + low := table[c] + if low != c { + res := make([]byte, n) + copy(res, s[:i]) + res[i] = low + for i++; i < n; i++ { + res[i] = table[s[i]] + } + return unsafeconv.UnsafeString(res) + } + } return s } - table := caseconv.ToLowerTable - for i := range n { - c := s[i] - low := table[c] - if low != c { - res := make([]byte, n) - copy(res, s[:i]) - res[i] = low - j := i + 1 - for ; j+3 < n; j += 4 { - res[j+0] = table[s[j+0]] - res[j+1] = table[s[j+1]] - res[j+2] = table[s[j+2]] - res[j+3] = table[s[j+3]] - } - for ; j < n; j++ { - res[j] = table[s[j]] - } - return unsafeconv.UnsafeString(res) - } + src := unsafeconv.UnsafeBytes(s) + i := caseconv.FirstUpperIndex(src) + if i < 0 { + return s } - return s + + res := make([]byte, n) + // Copy the unchanged prefix up to the word containing the first + // uppercase byte, then convert the rest word-at-a-time. + from := i &^ (caseconv.WordLen - 1) + copy(res, src[:from]) + caseconv.ToLowerCopy(res, src, from) + return unsafeconv.UnsafeString(res) } // ToUpper converts an ASCII string to upper-case without modifying the input. func ToUpper(s string) string { n := len(s) - if n == 0 { + if n < swarMinLen { + table := caseconv.ToUpperTable + for i := 0; i < n; i++ { + c := s[i] + up := table[c] + if up != c { + res := make([]byte, n) + copy(res, s[:i]) + res[i] = up + for i++; i < n; i++ { + res[i] = table[s[i]] + } + return unsafeconv.UnsafeString(res) + } + } return s } - table := caseconv.ToUpperTable - for i := range n { - c := s[i] - up := table[c] - if up != c { - res := make([]byte, n) - copy(res, s[:i]) - res[i] = up - j := i + 1 - for ; j+3 < n; j += 4 { - res[j+0] = table[s[j+0]] - res[j+1] = table[s[j+1]] - res[j+2] = table[s[j+2]] - res[j+3] = table[s[j+3]] - } - for ; j < n; j++ { - res[j] = table[s[j]] - } - return unsafeconv.UnsafeString(res) - } + src := unsafeconv.UnsafeBytes(s) + i := caseconv.FirstLowerIndex(src) + if i < 0 { + return s } - return s + + res := make([]byte, n) + from := i &^ (caseconv.WordLen - 1) + copy(res, src[:from]) + caseconv.ToUpperCopy(res, src, from) + return unsafeconv.UnsafeString(res) } // UnsafeToLower converts an ASCII string to lower-case by mutating its backing bytes in-place. @@ -72,26 +84,14 @@ func ToUpper(s string) string { // string is known to reference mutable memory. func UnsafeToLower(s string) string { b := unsafeconv.UnsafeBytes(s) - table := caseconv.ToLowerTable - n := len(b) - i := 0 - limit := n &^ 3 - for i < limit { - b0 := b[i+0] - b1 := b[i+1] - b2 := b[i+2] - b3 := b[i+3] - - b[i+0] = table[b0] - b[i+1] = table[b1] - b[i+2] = table[b2] - b[i+3] = table[b3] - i += 4 - } - for i < n { - b[i] = table[b[i]] - i++ + if len(b) < swarMinLen { + table := caseconv.ToLowerTable + for i := range b { + b[i] = table[b[i]] + } + return s } + caseconv.ToLowerInPlace(b) return s } @@ -100,25 +100,13 @@ func UnsafeToLower(s string) string { // string is known to reference mutable memory. func UnsafeToUpper(s string) string { b := unsafeconv.UnsafeBytes(s) - table := caseconv.ToUpperTable - n := len(b) - i := 0 - limit := n &^ 3 - for i < limit { - b0 := b[i+0] - b1 := b[i+1] - b2 := b[i+2] - b3 := b[i+3] - - b[i+0] = table[b0] - b[i+1] = table[b1] - b[i+2] = table[b2] - b[i+3] = table[b3] - i += 4 - } - for i < n { - b[i] = table[b[i]] - i++ + if len(b) < swarMinLen { + table := caseconv.ToUpperTable + for i := range b { + b[i] = table[b[i]] + } + return s } + caseconv.ToUpperInPlace(b) return s } diff --git a/src/vendor/github.com/gofiber/utils/v2/swar/swar.go b/src/vendor/github.com/gofiber/utils/v2/swar/swar.go new file mode 100644 index 00000000..6290a000 --- /dev/null +++ b/src/vendor/github.com/gofiber/utils/v2/swar/swar.go @@ -0,0 +1,146 @@ +// Package swar provides SWAR (SIMD within a register) primitives for +// processing eight ASCII bytes per uint64 word. The building blocks here are +// the ones the rest of gofiber/utils composes into case conversion, +// multi-needle scanning, and validation loops, and they are exported so that +// downstream packages (fiber itself, middleware) can fuse their own scans — +// e.g. finding a delimiter while simultaneously classifying the bytes before +// it — without re-deriving the bit tricks. +// +// Every operation yields per-lane results (exactly for the Match* masks, +// approximately for ZeroLanes, whose contract allows false positives above +// the first hit), so results are identical on little- and big-endian +// platforms. Load8 and Store8 always use little-endian lane order: lane 0 +// (the least significant byte) is the lowest byte index, which is what +// FirstLane and LastLane assume. The empty-mask sentinels differ on +// purpose: FirstLane(0) == 8 lets forward scans step past the current word +// arithmetically, while LastLane(0) == -1 is the conventional not-found +// result for reverse scans. +// +// No unsafe is used anywhere in this package. +package swar + +import ( + "math/bits" +) + +const ( + // WordLen is the SWAR word width in bytes. Word loops should iterate + // while i+WordLen <= len(s) and route shorter inputs to scalar code. + WordLen = 8 + + // Ones has 0x01 in every byte lane. + Ones = 0x0101010101010101 + // HighBits has 0x80 in every byte lane; w&HighBits != 0 iff some byte + // has its high bit set, and Match* masks equal HighBits iff every lane + // matched. + HighBits = 0x8080808080808080 + // LowSeven has 0x7F in every byte lane. + LowSeven = 0x7f7f7f7f7f7f7f7f +) + +// Load8 assembles s[i:i+8] into a little-endian uint64: s[i] becomes the +// least significant byte (lane 0). The caller must guarantee i+8 <= len(s); +// the reslice below turns a violation into a bounds panic. The 8-byte +// reslice pins the length so the constant-index reads are bounds-check free, +// and the compiler fuses them into a single 8-byte load on little-endian +// targets (verified for both the string and []byte instantiations). +func Load8[S ~string | ~[]byte](s S, i int) uint64 { + w := s[i : i+8] + return uint64(w[0]) | + uint64(w[1])<<8 | + uint64(w[2])<<16 | + uint64(w[3])<<24 | + uint64(w[4])<<32 | + uint64(w[5])<<40 | + uint64(w[6])<<48 | + uint64(w[7])<<56 +} + +// Store8 writes w into b[i:i+8] in Load8's lane order: lane 0 (the least +// significant byte) lands at the lowest byte index, so a Load8/Store8 round +// trip is the identity. The caller must guarantee i+8 <= len(b); the +// reslice below turns a violation into a bounds panic. Like Load8, the +// constant-index writes are bounds-check free and fuse into a single 8-byte +// store on little-endian targets. +func Store8(b []byte, i int, w uint64) { + d := b[i : i+8] + d[0] = byte(w) + d[1] = byte(w >> 8) + d[2] = byte(w >> 16) + d[3] = byte(w >> 24) + d[4] = byte(w >> 32) + d[5] = byte(w >> 40) + d[6] = byte(w >> 48) + d[7] = byte(w >> 56) +} + +// Broadcast returns a word with c in every byte lane, the needle form that +// ZeroLanes scans consume. Hoist the result out of word loops; it compiles +// to a single multiply. +func Broadcast(c byte) uint64 { + return uint64(c) * Ones +} + +// ToLowerWord lower-cases every 'A'..'Z' lane in w. All other bytes, +// including those >= 0x80, pass through unchanged — bit-identical to the +// ASCII case-conversion tables used elsewhere in this module. +func ToLowerWord(w uint64) uint64 { + return w | MatchRangeMask(w, 'A', 'Z')>>2 // 0x80>>2 == 0x20: set bit 5 +} + +// ToUpperWord upper-cases every 'a'..'z' lane in w. All other bytes, +// including those >= 0x80, pass through unchanged. +func ToUpperWord(w uint64) uint64 { + return w &^ (MatchRangeMask(w, 'a', 'z') >> 2) // clear bit 5 on matched lanes +} + +// ZeroLanes returns a mask whose lowest set lane is the lowest zero-byte +// lane of x; higher lanes may carry false positives (borrows propagate +// strictly upward from true zero lanes), and the mask is zero iff x has no +// zero byte. It is two ops cheaper than MatchByteMask, which makes it the +// right primitive for first-match scans, typically as +// ZeroLanes(w ^ Broadcast(c)) with the broadcast hoisted out of the word +// loop. Use MatchByteMask when every lane must be exact. +func ZeroLanes(x uint64) uint64 { + return (x - Ones) &^ x & HighBits +} + +// MatchByteMask returns a word with 0x80 set in exactly the lanes of w whose +// byte equals c, and zero in all other lanes. The result is per-lane exact +// (no false positives in lanes above a match), so it is safe to feed to +// LastLane or to arbitrary lane masking, not just first-match scans; for +// pure first-match scanning ZeroLanes is cheaper. +func MatchByteMask(w uint64, c byte) uint64 { + x := w ^ (uint64(c) * Ones) + // Setting the high bit before the per-lane subtraction keeps every lane + // >= 0x80, so borrows cannot cross lanes and the test stays exact: + // after the subtraction the high bit is clear only where the lane was 0, + // and OR-ing x back in rejects lanes that had their own high bit set. + return ^(((x | HighBits) - Ones) | x) & HighBits +} + +// MatchRangeMask returns a word with 0x80 set in exactly the lanes of w +// whose byte is within [lo, hi]. It requires lo <= hi <= 0x7F; lanes with +// the high bit set (bytes >= 0x80) never match. +func MatchRangeMask(w uint64, lo, hi byte) uint64 { + b := w & LowSeven + // After masking, every lane is <= 0x7F, so the biased additions below + // cannot carry into a neighboring lane. + ge := b + (0x80-uint64(lo))*Ones // high bit set where lane >= lo + gt := b + (0x80-uint64(hi)-1)*Ones // high bit set where lane > hi + return ge &^ gt &^ w & HighBits +} + +// FirstLane returns the index (0..7) of the lowest-addressed set lane in +// mask — the smallest byte index, given Load8's lane order — or 8 if mask +// is zero. mask must contain only 0x80 lane markers, as produced by the +// Match* functions. +func FirstLane(mask uint64) int { + return bits.TrailingZeros64(mask) >> 3 +} + +// LastLane returns the index (0..7) of the highest-addressed set lane in +// mask, or -1 if mask is zero. mask must contain only 0x80 lane markers. +func LastLane(mask uint64) int { + return (63 - bits.LeadingZeros64(mask)) >> 3 +} diff --git a/src/vendor/github.com/gofiber/utils/v2/time.go b/src/vendor/github.com/gofiber/utils/v2/time.go index 394a23c8..91b970af 100644 --- a/src/vendor/github.com/gofiber/utils/v2/time.go +++ b/src/vendor/github.com/gofiber/utils/v2/time.go @@ -8,7 +8,7 @@ import ( var ( timestamp atomic.Uint32 - updaterOnce sync.Once + updaterMu sync.Mutex stopUpdater chan struct{} updaterDone chan struct{} ) @@ -20,39 +20,46 @@ func Timestamp() uint32 { } // StartTimeStampUpdater launches a background goroutine that updates the cached timestamp every second. -// It is safe to call multiple times; only the first call will start the updater. +// It is safe to call multiple times and from multiple goroutines; only one updater runs at a time. func StartTimeStampUpdater() { - updaterOnce.Do(func() { - timestamp.Store(uint32(time.Now().Unix())) - stopUpdater = make(chan struct{}) - updaterDone = make(chan struct{}) - - go func() { - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - defer close(updaterDone) - - for { - select { - case <-ticker.C: - timestamp.Store(uint32(time.Now().Unix())) - case <-stopUpdater: - return - } + updaterMu.Lock() + defer updaterMu.Unlock() + if stopUpdater != nil { + return + } + + timestamp.Store(uint32(time.Now().Unix())) + stopUpdater = make(chan struct{}) + updaterDone = make(chan struct{}) + + go func(stop, done chan struct{}) { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + defer close(done) + + for { + select { + case <-ticker.C: + timestamp.Store(uint32(time.Now().Unix())) + case <-stop: + return } - }() - }) + } + }(stopUpdater, updaterDone) } // StopTimeStampUpdater stops the background updater goroutine. // Call this on app shutdown to avoid leaking goroutines. +// It is safe to call multiple times and from multiple goroutines. func StopTimeStampUpdater() { - if stopUpdater != nil { - close(stopUpdater) - <-updaterDone - // Reset the sync.Once so StartTimeStampUpdater can be called again - updaterOnce = sync.Once{} - stopUpdater = nil - updaterDone = nil + updaterMu.Lock() + defer updaterMu.Unlock() + if stopUpdater == nil { + return } + + close(stopUpdater) + <-updaterDone + stopUpdater = nil + updaterDone = nil } diff --git a/src/vendor/github.com/klauspost/compress/README.md b/src/vendor/github.com/klauspost/compress/README.md index fb023f2c..0e9f170d 100644 --- a/src/vendor/github.com/klauspost/compress/README.md +++ b/src/vendor/github.com/klauspost/compress/README.md @@ -27,6 +27,18 @@ Use the links above for more information on each. # changelog +* Jul 1st, 2026 [1.19.0](https://github.com/klauspost/compress/releases/tag/v1.19.0) + * zstd: Add true concurrent stream encodingin https://github.com/klauspost/compress/pull/1136 + * zstd: arm64 decoder asm by @lizthegrey in https://github.com/klauspost/compress/pull/1160 + * flate: Add inflate checkpoints in https://github.com/klauspost/compress/pull/1154 + * zstd: avoid unused BuildDict encoder allocation by @snissn in https://github.com/klauspost/compress/pull/1147 + * snappy/s2: Limit length of varint in `decodedLen` by @eustas in https://github.com/klauspost/compress/pull/1148 + * gzhttp: match qvalue parameter case-insensitively (RFC 7231) by @z9z in https://github.com/klauspost/compress/pull/1149 + * zip: add NameDecoder callback for legacy encoding rewrite by @SAY-5 in https://github.com/klauspost/compress/pull/1150 + * huff0: Allow building tables from histogram in https://github.com/klauspost/compress/pull/1155 + * huff0: Allow building table from oversized histogram in https://github.com/klauspost/compress/pull/1156 + * s2sx: Clean symlink targets in https://github.com/klauspost/compress/pull/1163 + * Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 diff --git a/src/vendor/github.com/klauspost/compress/flate/inflate_gen.go b/src/vendor/github.com/klauspost/compress/flate/inflate_gen.go index 2b2f993f..e80f1965 100644 --- a/src/vendor/github.com/klauspost/compress/flate/inflate_gen.go +++ b/src/vendor/github.com/klauspost/compress/flate/inflate_gen.go @@ -10,6 +10,15 @@ import ( "strings" ) +func peekBufio(fr *bufio.Reader) ([]byte, error) { + if fr.Buffered() == 0 { + if _, err := fr.Peek(1); err != nil && fr.Buffered() == 0 { + return nil, err + } + } + return fr.Peek(fr.Buffered()) +} + // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the @@ -527,6 +536,8 @@ func (f *decompressor) huffmanBufioReader() { // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict + pbuf, _ := fr.Peek(fr.Buffered()) + pos := 0 switch f.stepState { case stateInit: @@ -548,12 +559,19 @@ readLiteral: n := uint(f.hl.maxRead) for { for fnb < n { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - f.err = noEOF(err) - return + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -566,6 +584,7 @@ readLiteral: } if n <= fnb { if n == 0 { + fr.Discard(pos) f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") @@ -586,6 +605,7 @@ readLiteral: case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { + fr.Discard(pos) f.toRead = dict.readFlush() f.step = huffmanBufioReader f.stepState = stateInit @@ -594,6 +614,7 @@ readLiteral: } goto readLiteral case v == 256: + fr.Discard(pos) f.b, f.nb = fb, fnb f.finishBlock() return @@ -605,15 +626,22 @@ readLiteral: length = int(val.length) + 3 n := uint(val.extra) for fnb < n { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - if debugDecode { - fmt.Println("morebits n>0:", err) + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return } - f.err = err - return } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -622,6 +650,7 @@ readLiteral: fb >>= n & regSizeMaskUint32 fnb -= n default: + fr.Discard(pos) if debugDecode { fmt.Println(v, ">= maxNumLit") } @@ -633,15 +662,22 @@ readLiteral: var dist uint32 if f.hd == nil { for fnb < 5 { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - if debugDecode { - fmt.Println("morebits f.nb<5:", err) + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return } - f.err = err - return } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -660,12 +696,19 @@ readLiteral: // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - f.err = noEOF(err) - return + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -678,6 +721,7 @@ readLiteral: } if n <= fnb { if n == 0 { + fr.Discard(pos) f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") @@ -701,15 +745,22 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - if debugDecode { - fmt.Println("morebits f.nb= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb uint32(dict.histSize()) { + fr.Discard(pos) f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) @@ -752,6 +805,7 @@ copyHistory: f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { + fr.Discard(pos) f.toRead = dict.readFlush() f.step = huffmanBufioReader // We need to continue this work f.stepState = stateDict diff --git a/src/vendor/github.com/klauspost/compress/s2/encode_all.go b/src/vendor/github.com/klauspost/compress/s2/encode_all.go index 9d12c44f..794ec8a6 100644 --- a/src/vendor/github.com/klauspost/compress/s2/encode_all.go +++ b/src/vendor/github.com/klauspost/compress/s2/encode_all.go @@ -981,7 +981,7 @@ searchDict: cv = load64(src, s) continue } - } else if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + } else if repeat > 0 && uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { base := s + checkRep // Extend back for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { diff --git a/src/vendor/github.com/klauspost/compress/s2/reader.go b/src/vendor/github.com/klauspost/compress/s2/reader.go index 4d01c419..17443e2c 100644 --- a/src/vendor/github.com/klauspost/compress/s2/reader.go +++ b/src/vendor/github.com/klauspost/compress/s2/reader.go @@ -216,13 +216,18 @@ func (r *Reader) skippable(tmp []byte, n int, allowEOF bool, id uint8) (ok bool) return r.err == nil } if rs, ok := r.r.(io.ReadSeeker); ok { - _, err := rs.Seek(int64(n), io.SeekCurrent) - if err == nil { - return true - } - if err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { - r.err = ErrCorrupt - return false + if cur, err := rs.Seek(0, io.SeekCurrent); err == nil { + if end, err := rs.Seek(0, io.SeekEnd); err == nil { + if cur+int64(n) <= end { + if _, err := rs.Seek(cur+int64(n), io.SeekStart); err == nil { + return true + } + } + if _, err := rs.Seek(cur, io.SeekStart); err != nil { + r.err = ErrCorrupt + return false + } + } } } for n > 0 { diff --git a/src/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s b/src/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s index 661761fb..77ee3913 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s +++ b/src/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s @@ -28,7 +28,10 @@ TEXT ·buildDtable_asm(SB), $0-24 init_main_loop: ADD R8<<1, R1, R15 MOVH (R15), R9 - CMN $1, R9 + AND $0xffff, R9, R15 + MOVD $-1, R16 + AND $0xffff, R16, R16 + CMP R16, R15 BNE do_not_update_high_threshold ADD R7<<3, R5, R15 MOVB R8, 1(R15) @@ -126,9 +129,13 @@ build_table_main_table: RET build_table_check1_ok: - TST R1, R1 + AND $0xff, R1, R15 + AND $0xff, R1, R16 + TST R16, R15 BNE build_table_check2_ok - CMP R6, R7 + AND $0xffff, R7, R15 + AND $0xffff, R6, R16 + CMP R16, R15 BNE build_table_check2_ok MOVD ctx+8(FP), R0 MOVD R7, 24(R0) diff --git a/src/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s b/src/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s index 75b19d73..a468e5fc 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s +++ b/src/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s @@ -670,9 +670,9 @@ error_overread: MOVD R16, ret+24(FP) RET -// skipped sequenceDecs_decode_bmi2 (BMI2 not available on arm64) +// skipped sequenceDecs_decode_bmi2 (generic twin preferred on arm64) -// skipped sequenceDecs_decode_56_bmi2 (BMI2 not available on arm64) +// skipped sequenceDecs_decode_56_bmi2 (generic twin preferred on arm64) // func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool // Requires: SSE @@ -1966,7 +1966,7 @@ error_not_enough_space: MOVD R16, ret+24(FP) RET -// skipped sequenceDecs_decodeSync_bmi2 (BMI2 not available on arm64) +// skipped sequenceDecs_decodeSync_bmi2 (generic twin preferred on arm64) // func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // Requires: CMOV, SSE @@ -2702,4 +2702,4 @@ error_not_enough_space: MOVD R16, ret+24(FP) RET -// skipped sequenceDecs_decodeSync_safe_bmi2 (BMI2 not available on arm64) +// skipped sequenceDecs_decodeSync_safe_bmi2 (generic twin preferred on arm64) diff --git a/src/vendor/github.com/klauspost/compress/zstd/snappy.go b/src/vendor/github.com/klauspost/compress/zstd/snappy.go index 336c2889..36c56f36 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/snappy.go +++ b/src/vendor/github.com/klauspost/compress/zstd/snappy.go @@ -334,9 +334,10 @@ func decodeSnappy(blk *blockEnc, src []byte) error { return errUnsupportedLiteralLength } - //if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s { - // return ErrSnappyCorrupt - //} + if length > len(src)-s { + println("length > len(src)-s", length, len(src)-s) + return ErrSnappyCorrupt + } blk.literals = append(blk.literals, src[s:s+length]...) //println(length, "litLen") diff --git a/src/vendor/github.com/mattn/go-isatty/isatty_others.go b/src/vendor/github.com/mattn/go-isatty/isatty_others.go index b24a2fad..569113ea 100644 --- a/src/vendor/github.com/mattn/go-isatty/isatty_others.go +++ b/src/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -1,5 +1,5 @@ -//go:build (appengine || js || nacl || tinygo || wasm || wasip1 || wasip2) && !windows -// +build appengine js nacl tinygo wasm wasip1 wasip2 +//go:build (appengine || js || nacl || tinygo || wasm || wasip1 || wasip2 || haiku) && !windows +// +build appengine js nacl tinygo wasm wasip1 wasip2 haiku // +build !windows package isatty diff --git a/src/vendor/github.com/valyala/fasthttp/coarseTime.go b/src/vendor/github.com/valyala/fasthttp/coarsetime.go similarity index 100% rename from src/vendor/github.com/valyala/fasthttp/coarseTime.go rename to src/vendor/github.com/valyala/fasthttp/coarsetime.go diff --git a/src/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/src/vendor/golang.org/x/crypto/acme/autocert/autocert.go index 69461e31..bee6de9c 100644 --- a/src/vendor/golang.org/x/crypto/acme/autocert/autocert.go +++ b/src/vendor/golang.org/x/crypto/acme/autocert/autocert.go @@ -564,24 +564,21 @@ func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error { // If the domain is already being verified, it waits for the existing verification to complete. // Either way, createCert blocks for the duration of the whole process. func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) { - // TODO: maybe rewrite this whole piece using sync.Once - state, err := m.certState(ck) + state, owner, err := m.certState(ck) if err != nil { return nil, err } - // state may exist if another goroutine is already working on it - // in which case just wait for it to finish - if !state.locked { + // If another goroutine is already working on this state, wait for it + // to finish by taking the read lock + if !owner { state.RLock() defer state.RUnlock() return state.tlscert() } - // We are the first; state is locked. - // Unblock the readers when domain ownership is verified - // and we got the cert or the process failed. + // We are the first to work on this certKey, so state is write-locked. + // Unblock the readers when our work is complete. defer state.Unlock() - state.locked = false der, leaf, err := m.authorizedCert(ctx, state.key, ck) if err != nil { @@ -611,10 +608,15 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, return state.tlscert() } -// certState returns a new or existing certState. -// If a new certState is returned, state.exist is false and the state is locked. +// certState returns a new or existing certState along with a boolean +// indicating whether the caller is the owner of the state. +// +// The owner of the state is responsible for performing the ACME work and +// must unlock the state's write lock when done. Non-owner callers should +// wait on the state's read lock for the owner to finish. +// // The returned error is non-nil only in the case where a new state could not be created. -func (m *Manager) certState(ck certKey) (*certState, error) { +func (m *Manager) certState(ck certKey) (*certState, bool, error) { m.stateMu.Lock() defer m.stateMu.Unlock() if m.state == nil { @@ -622,7 +624,7 @@ func (m *Manager) certState(ck certKey) (*certState, error) { } // existing state if state, ok := m.state[ck]; ok { - return state, nil + return state, false, nil } // new locked state @@ -636,16 +638,13 @@ func (m *Manager) certState(ck certKey) (*certState, error) { key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } if err != nil { - return nil, err + return nil, false, err } - state := &certState{ - key: key, - locked: true, - } + state := &certState{key: key} state.Lock() // will be unlocked by m.certState caller m.state[ck] = state - return state, nil + return state, true, nil } // authorizedCert starts the domain ownership verification process and requests a new cert upon success. @@ -1035,10 +1034,9 @@ func (m *Manager) now() time.Time { // certState is ready when its mutex is unlocked for reading. type certState struct { sync.RWMutex - locked bool // locked for read/write - key crypto.Signer // private key for cert - cert [][]byte // DER encoding - leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil + key crypto.Signer // private key for cert + cert [][]byte // DER encoding + leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil } // tlscert creates a tls.Certificate from s.key and s.cert. diff --git a/src/vendor/golang.org/x/crypto/argon2/argon2.go b/src/vendor/golang.org/x/crypto/argon2/argon2.go index 2b65ec91..57ab8371 100644 --- a/src/vendor/golang.org/x/crypto/argon2/argon2.go +++ b/src/vendor/golang.org/x/crypto/argon2/argon2.go @@ -17,8 +17,8 @@ // It uses data-independent memory access, which is preferred for password // hashing and password-based key derivation. Argon2i requires more passes over // memory than Argon2id to protect from trade-off attacks. The recommended -// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive operations are time=3 and to -// use the maximum available memory. +// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive +// operations are time=3 and to use the maximum available memory. // // # Argon2id // @@ -26,11 +26,14 @@ // Argon2i and Argon2d. It uses data-independent memory access for the first // half of the first iteration over the memory and data-dependent memory access // for the rest. Argon2id is side-channel resistant and provides better brute- -// force cost savings due to time-memory tradeoffs than Argon2i. The recommended -// parameters for non-interactive operations (taken from [RFC 9106 Section 7.3]) are time=1 and to -// use the maximum available memory. +// force cost savings due to time-memory tradeoffs than Argon2i. [RFC 9106 +// Section 4] recommends time=1, memory=2*1024*1024 KiB (2 GiB), and threads=4 +// as the first recommended option. If much less memory is available, it +// recommends time=3, memory=64*1024 KiB (64 MiB), and threads=4 as the second +// recommended option. // // [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf +// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4 // [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3 package argon2 @@ -59,9 +62,9 @@ const ( // // key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32) // -// [RFC 9106 Section 7.3] recommends time=3, and memory=32*1024 as a sensible number. -// If using that amount of memory (32 MB) is not possible in some contexts then -// the time parameter can be increased to compensate. +// The example above uses time=3 and memory=32*1024. Argon2i generally +// requires more passes over memory than Argon2id. If in doubt, prefer IDKey +// and its Argon2id parameter recommendations. // // The time parameter specifies the number of passes over the memory and the // memory parameter specifies the size of the memory in KiB. For example @@ -69,8 +72,6 @@ const ( // adjusted to the number of available CPUs. The cost parameters should be // increased as memory latency and CPU parallelism increases. Remember to get a // good random salt. -// -// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3 func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen) } @@ -83,20 +84,20 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3 // For example, you can get a derived key for e.g. AES-256 (which needs a // 32-byte key) by doing: // -// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32) +// key := argon2.IDKey([]byte("some password"), salt, 1, 2*1024*1024, 4, 32) // -// [RFC 9106 Section 7.3] recommends time=1, and memory=64*1024 as a sensible number. -// If using that amount of memory (64 MB) is not possible in some contexts then -// the time parameter can be increased to compensate. +// The example above uses the first [RFC 9106 Section 4] recommended option. +// If much less memory is available, the second recommended option is time=3, +// memory=64*1024 KiB (64 MiB), and threads=4. // // The time parameter specifies the number of passes over the memory and the // memory parameter specifies the size of the memory in KiB. For example -// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be -// adjusted to the numbers of available CPUs. The cost parameters should be +// memory=2*1024*1024 sets the memory cost to ~2 GiB. The number of threads can +// be adjusted to the numbers of available CPUs. The cost parameters should be // increased as memory latency and CPU parallelism increases. Remember to get a // good random salt. // -// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3 +// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4 func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen) } diff --git a/src/vendor/golang.org/x/net/idna/idna.go b/src/vendor/golang.org/x/net/idna/idna.go index 22767125..e2f28fed 100644 --- a/src/vendor/golang.org/x/net/idna/idna.go +++ b/src/vendor/golang.org/x/net/idna/idna.go @@ -400,7 +400,11 @@ func (p *Profile) process(s string, toASCII bool) (string, error) { // Spec says keep the old label. continue } - if unicode16 && err == nil && len(u) > 0 && isASCII(u) { + if err == nil && len(u) > 0 && isASCII(u) { + // UTS 43 pre-revision 33 doesn't classify a xn-- label + // which contains only ASCII characters as an error, + // but that's a specification bug and a security issue. + // Always return an error in this case. err = punyError(enc) } isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight diff --git a/src/vendor/golang.org/x/sys/cpu/parse.go b/src/vendor/golang.org/x/sys/cpu/parse.go index 56a7e1a1..12a99af5 100644 --- a/src/vendor/golang.org/x/sys/cpu/parse.go +++ b/src/vendor/golang.org/x/sys/cpu/parse.go @@ -6,38 +6,50 @@ package cpu import "strconv" -// parseRelease parses a dot-separated version number. It follows the semver -// syntax, but allows the minor and patch versions to be elided. +// parseRelease parses a dot-separated version number from the prefix +// of rel. It returns ok=true only if at least the major and minor +// components were successfully parsed; the patch component is +// best-effort. Trailing vendor or build suffixes such as +// "-generic", "+", "_hi3535", or "-rc1" are ignored. // // This is a copy of the Go runtime's parseRelease from -// https://golang.org/cl/209597. +// https://golang.org/cl/209597, updated in https://golang.org/cl/781800. func parseRelease(rel string) (major, minor, patch int, ok bool) { - // Strip anything after a dash or plus. - for i := range len(rel) { - if rel[i] == '-' || rel[i] == '+' { - rel = rel[:i] - break + // next consumes a run of decimal digits from the front of rel, + // returning the parsed value. If the digits are followed by a + // '.', it is consumed and more is set so the caller knows to + // parse another component; otherwise scanning terminates and + // the rest of rel is discarded. + next := func() (n int, more, ok bool) { + i := 0 + for i < len(rel) && rel[i] >= '0' && rel[i] <= '9' { + i++ } - } - - next := func() (int, bool) { - for i := range len(rel) { - if rel[i] == '.' { - ver, err := strconv.Atoi(rel[:i]) - rel = rel[i+1:] - return ver, err == nil - } + if i == 0 { + return 0, false, false + } + n, err := strconv.Atoi(rel[:i]) + if err != nil { + return 0, false, false + } + if i < len(rel) && rel[i] == '.' { + rel = rel[i+1:] + return n, true, true } - ver, err := strconv.Atoi(rel) rel = "" - return ver, err == nil + return n, false, true + } + + var more bool + if major, more, ok = next(); !ok || !more { + return 0, 0, 0, false } - if major, ok = next(); !ok || rel == "" { - return + if minor, more, ok = next(); !ok { + return 0, 0, 0, false } - if minor, ok = next(); !ok || rel == "" { - return + if !more { + return major, minor, 0, true } - patch, ok = next() - return + patch, _, _ = next() + return major, minor, patch, true } diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux.go b/src/vendor/golang.org/x/sys/unix/syscall_linux.go index ce4d7ab1..21e2bfa3 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1874,6 +1874,7 @@ func Dup2(oldfd, newfd int) error { //sys Dup3(oldfd int, newfd int, flags int) (err error) //sysnb EpollCreate1(flag int) (fd int, err error) //sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Eventfd(initval uint, flags int) (fd int, err error) = SYS_EVENTFD2 //sys Exit(code int) = SYS_EXIT_GROUP //sys Fallocate(fd int, mode uint32, off int64, len int64) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_386.go index 506dafa7..210d545c 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_386.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_386.go @@ -20,7 +20,6 @@ func setTimeval(sec, usec int64) Timeval { // 64-bit file system and 32-bit uid calls // (386 default is 32-bit file system and 16-bit uid). -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64_64 //sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go index d557cf8d..a9a52f23 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go @@ -6,7 +6,6 @@ package unix -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index ecf92bfa..54474c20 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -44,7 +44,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { // 64-bit file system and 32-bit uid calls // (16-bit uid calls are not always supported in newer kernels) -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 //sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64 diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index 17373807..e9f30db9 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -8,7 +8,6 @@ package unix import "unsafe" -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go index a3fd1d0b..6f09ca20 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -8,7 +8,6 @@ package unix import "unsafe" -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go index 70963a95..ca3b5659 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go @@ -6,7 +6,6 @@ package unix -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go index c218ebd2..54ba667b 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go @@ -13,7 +13,6 @@ import ( func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64 diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go index e6c48500..ce462859 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go @@ -11,7 +11,6 @@ import ( "unsafe" ) -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 //sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64 diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go index 7286a9aa..33f7af38 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go @@ -6,7 +6,6 @@ package unix -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index fc5543c5..c658871e 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -8,7 +8,6 @@ package unix import "unsafe" -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go index 66f31210..2c858769 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go @@ -10,7 +10,6 @@ import ( "unsafe" ) -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go b/src/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go index 11d1f169..4964119a 100644 --- a/src/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +++ b/src/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go @@ -6,7 +6,6 @@ package unix -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) diff --git a/src/vendor/golang.org/x/sys/unix/zerrors_linux.go b/src/vendor/golang.org/x/sys/unix/zerrors_linux.go index 9d72a6b7..5bb51d7a 100644 --- a/src/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/src/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1359,6 +1359,7 @@ const ( FAN_UNLIMITED_MARKS = 0x20 FAN_UNLIMITED_QUEUE = 0x10 FD_CLOEXEC = 0x1 + FD_PIDFS_ROOT = -0x2712 FD_SETSIZE = 0x400 FF0 = 0x0 FIB_RULE_DEV_DETACHED = 0x8 @@ -1970,6 +1971,8 @@ const ( MADV_DONTNEED = 0x4 MADV_DONTNEED_LOCKED = 0x18 MADV_FREE = 0x8 + MADV_GUARD_INSTALL = 0x66 + MADV_GUARD_REMOVE = 0x67 MADV_HUGEPAGE = 0xe MADV_HWPOISON = 0x64 MADV_KEEPONFORK = 0x13 @@ -2114,7 +2117,7 @@ const ( MS_NOSEC = 0x10000000 MS_NOSUID = 0x2 MS_NOSYMFOLLOW = 0x100 - MS_NOUSER = -0x80000000 + MS_NOUSER = 0x80000000 MS_POSIXACL = 0x10000 MS_PRIVATE = 0x40000 MS_RDONLY = 0x1 @@ -3786,6 +3789,9 @@ const ( TCPOPT_TIMESTAMP = 0x8 TCPOPT_TSTAMP_HDR = 0x101080a TCPOPT_WINDOW = 0x3 + TCP_AO_KEYF_EXCLUDE_OPT = 0x2 + TCP_AO_KEYF_IFINDEX = 0x1 + TCP_AO_MAXKEYLEN = 0x50 TCP_CC_INFO = 0x1a TCP_CM_INQ = 0x24 TCP_CONGESTION = 0xd diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 80f40e40..5788c2a5 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -700,6 +700,23 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Eventfd(initval uint, flags int) (fd int, err error) { r0, _, e1 := Syscall(SYS_EVENTFD2, uintptr(initval), uintptr(flags), 0) fd = int(r0) diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go index 4def3e9f..254f3398 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64_64, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice)) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go index fef2bc8b..27c05db1 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go index a9fd76a8..840d85bf 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go @@ -213,23 +213,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go index 46006502..fe414498 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go index c8987d26..eb358ce0 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go index 921f4306..c437622f 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset>>32), uintptr(offset), uintptr(length>>32), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go index 44f06782..bc4ca255 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go index e7fa0abf..5051435c 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go index 8c512567..33aa5418 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go index 7392fd45..3bef8ef1 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go index 41180434..fc1bd4e2 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go index 40c6ce7a..d78fe7da 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go index 2cfe34ad..76dcf87d 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go index 61e6f070..2cf020f2 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go index 834b8420..52763762 100644 --- a/src/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go +++ b/src/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go @@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { diff --git a/src/vendor/golang.org/x/sys/windows/security_windows.go b/src/vendor/golang.org/x/sys/windows/security_windows.go index 6c955cea..78362156 100644 --- a/src/vendor/golang.org/x/sys/windows/security_windows.go +++ b/src/vendor/golang.org/x/sys/windows/security_windows.go @@ -1109,17 +1109,53 @@ const ( ) // This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions. +// +// Go pointers stored in a TrusteeValue must be pinned using [runtime.Pinner] +// for the lifetime of the TrusteeValue. type TrusteeValue uintptr +// TrusteeValueFromString is unsafe and should not be used. +// +// It returns a uintptr containing a reference to newly-allocated memory +// which will be freed by the garbage collector. +// There is no way for the caller to safely reference this memory. +// +// To create a [TrusteeValue] from a string, use: +// +// p, err := windows.UTF16PtrFromString(s) +// if err != nil { +// // handle error +// } +// +// // Pin the string for as long as it is used. +// var pinner runtime.Pinner +// pinner.Pin(p) +// defer pinner.Unpin() +// +// tv := TrusteeValue(unsafe.Pointer(p)) +// +// Deprecated: TrusteeValueFromString is unsafe and should not be used. func TrusteeValueFromString(str string) TrusteeValue { return TrusteeValue(unsafe.Pointer(StringToUTF16Ptr(str))) } + +// TrusteeValueFromSID returns a [TrusteeValue] referencing sid. +// +// The caller must pin sid using a [runtime.Pinner] for the lifetime of the TrusteeValue. func TrusteeValueFromSID(sid *SID) TrusteeValue { return TrusteeValue(unsafe.Pointer(sid)) } + +// TrusteeValueFromObjectsAndSid returns a [TrusteeValue] referencing objectsAndSid. +// +// The caller must pin objectsAndSid using a [runtime.Pinner] for the lifetime of the TrusteeValue. func TrusteeValueFromObjectsAndSid(objectsAndSid *OBJECTS_AND_SID) TrusteeValue { return TrusteeValue(unsafe.Pointer(objectsAndSid)) } + +// TrusteeValueFromObjectsAndName returns a [TrusteeValue] referencing objectsAndName. +// +// The caller must pin objectsAndName using a [runtime.Pinner] for the lifetime of the TrusteeValue. func TrusteeValueFromObjectsAndName(objectsAndName *OBJECTS_AND_NAME) TrusteeValue { return TrusteeValue(unsafe.Pointer(objectsAndName)) } diff --git a/src/vendor/golang.org/x/sys/windows/syscall_windows.go b/src/vendor/golang.org/x/sys/windows/syscall_windows.go index 9755bca9..e6966b4c 100644 --- a/src/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/src/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -1728,11 +1728,15 @@ func (s *NTUnicodeString) String() string { // the more common *uint16 string type. func NewNTString(s string) (*NTString, error) { var nts NTString - s8, err := BytePtrFromString(s) + s8, err := ByteSliceFromString(s) if err != nil { return nil, err } - RtlInitString(&nts, s8) + // The source string plus its terminating NUL must fit within MAX_USHORT. + if len(s8) > MAX_USHORT { + return nil, syscall.EINVAL + } + RtlInitString(&nts, &s8[0]) return &nts, nil } diff --git a/src/vendor/golang.org/x/sys/windows/types_windows.go b/src/vendor/golang.org/x/sys/windows/types_windows.go index d2574a73..75a50b31 100644 --- a/src/vendor/golang.org/x/sys/windows/types_windows.go +++ b/src/vendor/golang.org/x/sys/windows/types_windows.go @@ -169,6 +169,7 @@ const ( FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192 FORMAT_MESSAGE_MAX_WIDTH_MASK = 255 + MAX_USHORT = 0xffff MAX_PATH = 260 MAX_LONG_PATH = 32768 diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 475a0dde..5378b47b 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -46,7 +46,7 @@ github.com/go-webauthn/webauthn/webauthn ## explicit; go 1.25.0 github.com/go-webauthn/x/encoding/asn1 github.com/go-webauthn/x/revoke -# github.com/gofiber/contrib/v3/websocket v1.2.1 +# github.com/gofiber/contrib/v3/websocket v1.2.2 ## explicit; go 1.25.0 github.com/gofiber/contrib/v3/websocket # github.com/gofiber/fiber/v3 v3.4.0 @@ -68,16 +68,17 @@ github.com/gofiber/fiber/v3/middleware/logger github.com/gofiber/fiber/v3/middleware/recover github.com/gofiber/fiber/v3/middleware/requestid github.com/gofiber/fiber/v3/middleware/static -# github.com/gofiber/schema v1.8.0 +# github.com/gofiber/schema v1.8.3 ## explicit; go 1.25.0 github.com/gofiber/schema -# github.com/gofiber/utils/v2 v2.1.1 +# github.com/gofiber/utils/v2 v2.2.0 ## explicit; go 1.25.0 github.com/gofiber/utils/v2 github.com/gofiber/utils/v2/bytes github.com/gofiber/utils/v2/internal/caseconv github.com/gofiber/utils/v2/internal/unsafeconv github.com/gofiber/utils/v2/strings +github.com/gofiber/utils/v2/swar # github.com/golang-jwt/jwt/v5 v5.3.1 ## explicit; go 1.21 github.com/golang-jwt/jwt/v5 @@ -118,7 +119,7 @@ github.com/jackc/puddle/v2/internal/genstack # github.com/joho/godotenv v1.5.1 ## explicit; go 1.12 github.com/joho/godotenv -# github.com/klauspost/compress v1.19.0 +# github.com/klauspost/compress v1.19.1 ## explicit; go 1.24 github.com/klauspost/compress github.com/klauspost/compress/flate @@ -147,8 +148,8 @@ github.com/looplab/fsm # github.com/mattn/go-colorable v0.1.15 ## explicit; go 1.18 github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.22 -## explicit; go 1.21 +# github.com/mattn/go-isatty v0.0.23 +## explicit; go 1.20 github.com/mattn/go-isatty # github.com/mfridman/interpolate v0.0.2 ## explicit; go 1.19 @@ -269,7 +270,7 @@ go.uber.org/multierr # go.yaml.in/yaml/v3 v3.0.4 ## explicit; go 1.16 go.yaml.in/yaml/v3 -# golang.org/x/crypto v0.53.0 +# golang.org/x/crypto v0.54.0 ## explicit; go 1.25.0 golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert @@ -284,7 +285,7 @@ golang.org/x/image/font/opentype golang.org/x/image/font/sfnt golang.org/x/image/math/fixed golang.org/x/image/vector -# golang.org/x/net v0.56.0 +# golang.org/x/net v0.57.0 ## explicit; go 1.25.0 golang.org/x/net/html golang.org/x/net/html/atom @@ -298,7 +299,7 @@ golang.org/x/net/publicsuffix ## explicit; go 1.25.0 golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.46.0 +# golang.org/x/sys v0.47.0 ## explicit; go 1.25.0 golang.org/x/sys/cpu golang.org/x/sys/unix