87 lines
2.5 KiB
Go
87 lines
2.5 KiB
Go
// 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.
|
|
|
|
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
|
|
}
|