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  }