mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-04 10:57:50 +08:00
96752afa4d
Struct map is a mechanism to let Go code expose simple structs to Elvish code. The difference between struct maps and maps is convenience for Go code; they also have different performance characteristics, but since struct maps are always quite small, the difference is not meaningful for Elvish's use cases. As a result, there is no good reason that Elvish code needs to be aware of the difference between struct maps and normal maps. Making them indistinguishable to Elvish code simplifies the language. This commit does the following: - Change Equal, Hash, Kind and Repr to treat struct maps like maps. - Change Assoc and Dissoc to "promote" struct maps to maps. - Remove the custom Repr method of parse.Source. - Update documentation to reflect this change.
78 lines
2.0 KiB
Go
78 lines
2.0 KiB
Go
package vals
|
|
|
|
import (
|
|
"math"
|
|
"math/big"
|
|
|
|
"src.elv.sh/pkg/persistent/hash"
|
|
"src.elv.sh/pkg/persistent/hashmap"
|
|
)
|
|
|
|
// Hasher wraps the Hash method.
|
|
type Hasher interface {
|
|
// Hash computes the hash code of the receiver.
|
|
Hash() uint32
|
|
}
|
|
|
|
// Hash returns the 32-bit hash of a value. It is implemented for the builtin
|
|
// 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).
|
|
func Hash(v any) uint32 {
|
|
switch v := v.(type) {
|
|
case bool:
|
|
if v {
|
|
return 1
|
|
}
|
|
return 0
|
|
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()))
|
|
case float64:
|
|
return hash.UInt64(math.Float64bits(v))
|
|
case string:
|
|
return hash.String(v)
|
|
case Hasher:
|
|
return v.Hash()
|
|
case File:
|
|
return hash.UIntPtr(v.Fd())
|
|
case List:
|
|
h := hash.DJBInit
|
|
for it := v.Iterator(); it.HasElem(); it.Next() {
|
|
h = hash.DJBCombine(h, Hash(it.Elem()))
|
|
}
|
|
return h
|
|
case Map:
|
|
return hashMap(v.Iterator())
|
|
case StructMap:
|
|
return hashMap(iterateStructMap(v))
|
|
}
|
|
return 0
|
|
}
|
|
|
|
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
|
|
}
|