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  }