2018-02-15 17:14:05 +08:00
|
|
|
package vals
|
2018-01-25 07:28:50 +08:00
|
|
|
|
2018-01-28 01:26:22 +08:00
|
|
|
import (
|
2019-04-20 00:40:45 +08:00
|
|
|
"math"
|
2021-06-28 00:01:12 +08:00
|
|
|
"math/big"
|
2019-04-20 00:40:45 +08:00
|
|
|
|
2021-05-04 05:17:46 +08:00
|
|
|
"src.elv.sh/pkg/persistent/hash"
|
2023-07-15 06:45:25 +08:00
|
|
|
"src.elv.sh/pkg/persistent/hashmap"
|
2018-01-28 01:26:22 +08:00
|
|
|
)
|
2018-01-25 09:40:15 +08:00
|
|
|
|
2018-01-25 07:57:58 +08:00
|
|
|
// Hasher wraps the Hash method.
|
|
|
|
type Hasher interface {
|
|
|
|
// Hash computes the hash code of the receiver.
|
|
|
|
Hash() uint32
|
|
|
|
}
|
|
|
|
|
2018-01-27 23:51:37 +08:00
|
|
|
// Hash returns the 32-bit hash of a value. It is implemented for the builtin
|
2019-04-20 00:40:45 +08:00
|
|
|
// types bool and string, the File, List, Map types, StructMap types, and types
|
|
|
|
// satisfying the Hasher interface. For other values, it returns 0 (which is OK
|
|
|
|
// in terms of correctness).
|
2022-03-20 23:50:25 +08:00
|
|
|
func Hash(v any) uint32 {
|
2018-01-25 09:40:15 +08:00
|
|
|
switch v := v.(type) {
|
2018-01-27 23:51:37 +08:00
|
|
|
case bool:
|
|
|
|
if v {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
return 0
|
2021-06-28 00:01:12 +08:00
|
|
|
case int:
|
|
|
|
return hash.UIntPtr(uintptr(v))
|
|
|
|
case *big.Int:
|
|
|
|
h := hash.DJBCombine(hash.DJBInit, uint32(v.Sign()))
|
|
|
|
for _, word := range v.Bits() {
|
|
|
|
h = hash.DJBCombine(h, hash.UIntPtr(uintptr(word)))
|
|
|
|
}
|
|
|
|
return h
|
|
|
|
case *big.Rat:
|
|
|
|
return hash.DJB(Hash(v.Num()), Hash(v.Denom()))
|
2019-04-20 00:40:45 +08:00
|
|
|
case float64:
|
|
|
|
return hash.UInt64(math.Float64bits(v))
|
2019-04-19 08:03:56 +08:00
|
|
|
case string:
|
|
|
|
return hash.String(v)
|
2020-06-28 08:47:31 +08:00
|
|
|
case Hasher:
|
|
|
|
return v.Hash()
|
2019-04-19 21:25:15 +08:00
|
|
|
case File:
|
|
|
|
return hash.UIntPtr(v.Fd())
|
2019-04-19 19:24:45 +08:00
|
|
|
case List:
|
2018-01-28 01:26:22 +08:00
|
|
|
h := hash.DJBInit
|
|
|
|
for it := v.Iterator(); it.HasElem(); it.Next() {
|
|
|
|
h = hash.DJBCombine(h, Hash(it.Elem()))
|
|
|
|
}
|
|
|
|
return h
|
2019-04-19 19:24:45 +08:00
|
|
|
case Map:
|
2023-07-15 06:45:25 +08:00
|
|
|
return hashMap(v.Iterator())
|
2019-04-20 00:40:45 +08:00
|
|
|
case StructMap:
|
2023-07-15 06:45:25 +08:00
|
|
|
return hashMap(iterateStructMap(v))
|
2018-01-25 07:57:58 +08:00
|
|
|
}
|
|
|
|
return 0
|
2018-01-25 07:28:50 +08:00
|
|
|
}
|
2023-07-15 06:45:25 +08:00
|
|
|
|
|
|
|
func hashMap(it hashmap.Iterator) uint32 {
|
|
|
|
// The iteration order of maps only depends on the hash of the keys. It is
|
|
|
|
// almost deterministic, with only one exception: when two keys have the
|
|
|
|
// same hash, they get produced in insertion order. As a result, it is
|
|
|
|
// possible for two maps that should be considered equal to produce entries
|
|
|
|
// in different orders.
|
|
|
|
//
|
|
|
|
// So instead of using hash.DJBCombine, combine the hash result from each
|
|
|
|
// key-value pair by summing, so that the order doesn't matter.
|
|
|
|
//
|
|
|
|
// TODO: This may not have very good hashing properties.
|
|
|
|
var h uint32
|
|
|
|
for ; it.HasElem(); it.Next() {
|
|
|
|
k, v := it.Elem()
|
|
|
|
h += hash.DJB(Hash(k), Hash(v))
|
|
|
|
}
|
|
|
|
return h
|
|
|
|
}
|