github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/key/hash.go (about) 1 // Copyright (c) 2016 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 package key 6 7 import ( 8 "encoding/binary" 9 "hash/maphash" 10 "math" 11 "unsafe" 12 ) 13 14 //go:noescape 15 //go:linkname strhash runtime.strhash 16 func strhash(a unsafe.Pointer, h uintptr) uintptr 17 18 func _strhash(s string) uintptr { 19 return strhash(unsafe.Pointer(&s), 0) 20 } 21 22 //go:noescape 23 //go:linkname nilinterhash runtime.nilinterhash 24 func nilinterhash(a unsafe.Pointer, h uintptr) uintptr 25 26 func _nilinterhash(v interface{}) uintptr { 27 return nilinterhash(unsafe.Pointer(&v), 0) 28 } 29 30 // Hash will hash a key. Can be used with 31 // [github.com/aristanetworks/gomap.Map]. 32 func Hash(seed maphash.Seed, k Key) uint64 { 33 var buf [8]byte 34 switch v := k.(type) { 35 case mapKey: 36 return hashMapKey(seed, v) 37 case interfaceKey: 38 s := v.Hash() 39 // Mix up the hash to ensure it covers 64-bits 40 binary.LittleEndian.PutUint64(buf[:8], uint64(s)) 41 return hashBytes(seed, buf[:8]) 42 case strKey: 43 return hashString(seed, string(v)) 44 case bytesKey: 45 return hashBytes(seed, []byte(v)) 46 case int8Key: 47 buf[0] = byte(v) 48 return hashBytes(seed, buf[:1]) 49 case int16Key: 50 binary.LittleEndian.PutUint16(buf[:2], uint16(v)) 51 return hashBytes(seed, buf[:2]) 52 case int32Key: 53 binary.LittleEndian.PutUint32(buf[:4], uint32(v)) 54 return hashBytes(seed, buf[:4]) 55 case int64Key: 56 binary.LittleEndian.PutUint64(buf[:8], uint64(v)) 57 return hashBytes(seed, buf[:8]) 58 case uint8Key: 59 buf[0] = byte(v) 60 return hashBytes(seed, buf[:1]) 61 case uint16Key: 62 binary.LittleEndian.PutUint16(buf[:2], uint16(v)) 63 return hashBytes(seed, buf[:2]) 64 case uint32Key: 65 binary.LittleEndian.PutUint32(buf[:4], uint32(v)) 66 return hashBytes(seed, buf[:4]) 67 case uint64Key: 68 binary.LittleEndian.PutUint64(buf[:8], uint64(v)) 69 return hashBytes(seed, buf[:8]) 70 case float32Key: 71 binary.LittleEndian.PutUint32(buf[:4], math.Float32bits(float32(v))) 72 return hashBytes(seed, buf[:4]) 73 case float64Key: 74 binary.LittleEndian.PutUint64(buf[:8], math.Float64bits(float64(v))) 75 return hashBytes(seed, buf[:8]) 76 case boolKey: 77 if v { 78 buf[0] = 1 79 } 80 return hashBytes(seed, buf[:1]) 81 case sliceKey: 82 return hashSliceKey(seed, v) 83 case pointerKey: 84 return hashSliceKey(seed, v.sliceKey) 85 case pathKey: 86 return hashSliceKey(seed, v.sliceKey) 87 case nilKey: 88 return hashBytes(seed, nil) 89 case Hashable: 90 // Mix up the hash to ensure it covers 64-bits 91 binary.LittleEndian.PutUint64(buf[:8], v.Hash()) 92 return hashBytes(seed, buf[:8]) 93 default: 94 s := _nilinterhash(v.Key()) 95 binary.LittleEndian.PutUint64(buf[:8], uint64(s)) 96 return hashBytes(seed, buf[:8]) 97 } 98 } 99 100 func hashMapKey(seed maphash.Seed, m mapKey) uint64 { 101 var buf [8]byte 102 var h maphash.Hash 103 h.SetSeed(seed) 104 var s uint64 105 for k, v := range m { 106 h.WriteString(k) 107 binary.BigEndian.PutUint64(buf[:8], uint64(HashInterface(v))) 108 h.Write(buf[:8]) 109 // combine hashes with addition so that order doesn't 110 // matter 111 s += h.Sum64() 112 h.Reset() 113 } 114 return s 115 116 } 117 118 func hashSliceKey(seed maphash.Seed, s sliceKey) uint64 { 119 var buf [8]byte 120 var h maphash.Hash 121 h.SetSeed(seed) 122 for _, v := range s { 123 binary.BigEndian.PutUint64(buf[:8], uint64(HashInterface(v))) 124 h.Write(buf[:8]) 125 } 126 return h.Sum64() 127 }