src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/vals/hash.go (about) 1 package vals 2 3 import ( 4 "math" 5 "math/big" 6 7 "src.elv.sh/pkg/persistent/hash" 8 "src.elv.sh/pkg/persistent/hashmap" 9 ) 10 11 // Hasher wraps the Hash method. 12 type Hasher interface { 13 // Hash computes the hash code of the receiver. 14 Hash() uint32 15 } 16 17 // Hash returns the 32-bit hash of a value. It is implemented for the builtin 18 // types bool and string, the File, List, Map types, StructMap types, and types 19 // satisfying the Hasher interface. For other values, it returns 0 (which is OK 20 // in terms of correctness). 21 func Hash(v any) uint32 { 22 switch v := v.(type) { 23 case bool: 24 if v { 25 return 1 26 } 27 return 0 28 case int: 29 return hash.UIntPtr(uintptr(v)) 30 case *big.Int: 31 h := hash.DJBCombine(hash.DJBInit, uint32(v.Sign())) 32 for _, word := range v.Bits() { 33 h = hash.DJBCombine(h, hash.UIntPtr(uintptr(word))) 34 } 35 return h 36 case *big.Rat: 37 return hash.DJB(Hash(v.Num()), Hash(v.Denom())) 38 case float64: 39 return hash.UInt64(math.Float64bits(v)) 40 case string: 41 return hash.String(v) 42 case Hasher: 43 return v.Hash() 44 case File: 45 return hash.UIntPtr(v.Fd()) 46 case List: 47 h := hash.DJBInit 48 for it := v.Iterator(); it.HasElem(); it.Next() { 49 h = hash.DJBCombine(h, Hash(it.Elem())) 50 } 51 return h 52 case Map: 53 return hashMap(v.Iterator()) 54 case StructMap: 55 return hashMap(iterateStructMap(v)) 56 } 57 return 0 58 } 59 60 func hashMap(it hashmap.Iterator) uint32 { 61 // The iteration order of maps only depends on the hash of the keys. It is 62 // almost deterministic, with only one exception: when two keys have the 63 // same hash, they get produced in insertion order. As a result, it is 64 // possible for two maps that should be considered equal to produce entries 65 // in different orders. 66 // 67 // So instead of using hash.DJBCombine, combine the hash result from each 68 // key-value pair by summing, so that the order doesn't matter. 69 // 70 // TODO: This may not have very good hashing properties. 71 var h uint32 72 for ; it.HasElem(); it.Next() { 73 k, v := it.Elem() 74 h += hash.DJB(Hash(k), Hash(v)) 75 } 76 return h 77 }