| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build goexperiment.jsonv2 |
| |
| package json |
| |
| import ( |
| "encoding/binary" |
| "math/bits" |
| ) |
| |
| // stringCache is a cache for strings converted from a []byte. |
| type stringCache = [256]string // 256*unsafe.Sizeof(string("")) => 4KiB |
| |
| // makeString returns the string form of b. |
| // It returns a pre-allocated string from c if present, otherwise |
| // it allocates a new string, inserts it into the cache, and returns it. |
| func makeString(c *stringCache, b []byte) string { |
| const ( |
| minCachedLen = 2 // single byte strings are already interned by the runtime |
| maxCachedLen = 256 // large enough for UUIDs, IPv6 addresses, SHA-256 checksums, etc. |
| ) |
| if c == nil || len(b) < minCachedLen || len(b) > maxCachedLen { |
| return string(b) |
| } |
| |
| // Compute a hash from the fixed-width prefix and suffix of the string. |
| // This ensures hashing a string is a constant time operation. |
| var h uint32 |
| switch { |
| case len(b) >= 8: |
| lo := binary.LittleEndian.Uint64(b[:8]) |
| hi := binary.LittleEndian.Uint64(b[len(b)-8:]) |
| h = hash64(uint32(lo), uint32(lo>>32)) ^ hash64(uint32(hi), uint32(hi>>32)) |
| case len(b) >= 4: |
| lo := binary.LittleEndian.Uint32(b[:4]) |
| hi := binary.LittleEndian.Uint32(b[len(b)-4:]) |
| h = hash64(lo, hi) |
| case len(b) >= 2: |
| lo := binary.LittleEndian.Uint16(b[:2]) |
| hi := binary.LittleEndian.Uint16(b[len(b)-2:]) |
| h = hash64(uint32(lo), uint32(hi)) |
| } |
| |
| // Check the cache for the string. |
| i := h % uint32(len(*c)) |
| if s := (*c)[i]; s == string(b) { |
| return s |
| } |
| s := string(b) |
| (*c)[i] = s |
| return s |
| } |
| |
| // hash64 returns the hash of two uint32s as a single uint32. |
| func hash64(lo, hi uint32) uint32 { |
| // If avalanche=true, this is identical to XXH32 hash on a 8B string: |
| // var b [8]byte |
| // binary.LittleEndian.PutUint32(b[:4], lo) |
| // binary.LittleEndian.PutUint32(b[4:], hi) |
| // return xxhash.Sum32(b[:]) |
| const ( |
| prime1 = 0x9e3779b1 |
| prime2 = 0x85ebca77 |
| prime3 = 0xc2b2ae3d |
| prime4 = 0x27d4eb2f |
| prime5 = 0x165667b1 |
| ) |
| h := prime5 + uint32(8) |
| h += lo * prime3 |
| h = bits.RotateLeft32(h, 17) * prime4 |
| h += hi * prime3 |
| h = bits.RotateLeft32(h, 17) * prime4 |
| // Skip final mix (avalanche) step of XXH32 for performance reasons. |
| // Empirical testing shows that the improvements in unbiased distribution |
| // does not outweigh the extra cost in computational complexity. |
| const avalanche = false |
| if avalanche { |
| h ^= h >> 15 |
| h *= prime2 |
| h ^= h >> 13 |
| h *= prime3 |
| h ^= h >> 16 |
| } |
| return h |
| } |