From 91980cbab5aab98ed4d04402c8efd17635add06e Mon Sep 17 00:00:00 2001 From: Roman Bataev Date: Fri, 2 Sep 2016 22:40:20 -0700 Subject: [PATCH] Add Hash methods --- core/hash_map.go | 26 +++++++++++ core/object.go | 117 ++++++++++++++++++++++++++++++++++++++--------- run.sh | 2 +- 3 files changed, 122 insertions(+), 23 deletions(-) create mode 100644 core/hash_map.go diff --git a/core/hash_map.go b/core/hash_map.go new file mode 100644 index 000000000..63a420616 --- /dev/null +++ b/core/hash_map.go @@ -0,0 +1,26 @@ +package core + +type ( + Box struct { + val Object + } + Node interface { + assoc(shift int, hash int, key Object, val Object, addedLeaf Box) Node + without(shift int, hash int, key Object) Node + find(shift int, hash int, key Object) Pair + tryFind(shift int, hash int, key Object, notFound Object) Object + nodeSeq() Seq + kvreduce(f Callable, init Object) Object + fold(combinef Callable, reducef Callable, fjtask Callable, fjfork Callable, fjjoin Callable) Object + } + HashMap struct { + InfoHolder + MetaHolder + count int + root Node + hasNull bool + nullValue Object + } +) + +var EmptyHashMap = &HashMap{} diff --git a/core/object.go b/core/object.go index 59887f23b..eeb0eff43 100644 --- a/core/object.go +++ b/core/object.go @@ -5,10 +5,14 @@ package core import ( + "encoding/gob" "fmt" + "hash" + "hash/fnv" "math/big" "reflect" "strings" + "unsafe" ) type ( @@ -29,6 +33,7 @@ type ( GetInfo() *ObjectInfo WithInfo(*ObjectInfo) Object GetType() *Type + // Hash() uint32 } Conjable interface { Object @@ -189,7 +194,7 @@ func init() { TYPES["Symbol"] = &Type{name: "Symbol", reflectType: reflect.TypeOf((*Symbol)(nil)).Elem()} TYPES["Regex"] = &Type{name: "Regex", reflectType: reflect.TypeOf((*Regex)(nil)).Elem()} TYPES["Var"] = &Type{name: "Var", reflectType: reflect.TypeOf((*Var)(nil))} - TYPES["Proc"] = &Type{name: "Fn", reflectType: reflect.TypeOf((*Proc)(nil)).Elem()} + TYPES["Proc"] = &Type{name: "Proc", reflectType: reflect.TypeOf((*Proc)(nil)).Elem()} TYPES["Fn"] = &Type{name: "Fn", reflectType: reflect.TypeOf((*Fn)(nil))} TYPES["ExInfo"] = &Type{name: "ExInfo", reflectType: reflect.TypeOf((*ExInfo)(nil))} TYPES["RecurBindings"] = &Type{name: "RecurBindings", reflectType: reflect.TypeOf((*RecurBindings)(nil)).Elem()} @@ -217,6 +222,13 @@ func init() { TYPES["Comparator"] = &Type{name: "Comparator", reflectType: reflect.TypeOf((*Comparator)(nil)).Elem()} } +var hasher hash.Hash32 = fnv.New32a() + +func getHash() hash.Hash32 { + hasher.Reset() + return hasher +} + func panicArity(n int) { name := RT.currentExpr.(Traceable).Name() panic(RT.NewError(fmt.Sprintf("Wrong number of args (%d) passed to %s", n, name))) @@ -257,6 +269,37 @@ func (d *Delay) GetType() *Type { return TYPES["Delay"] } +func hashPtr(ptr uintptr) uint32 { + h := getHash() + b := make([]byte, unsafe.Sizeof(ptr)) + b[0] = byte(ptr) + b[1] = byte(ptr >> 8) + b[2] = byte(ptr >> 16) + b[3] = byte(ptr >> 24) + if unsafe.Sizeof(ptr) == 8 { + b[4] = byte(ptr >> 32) + b[5] = byte(ptr >> 40) + b[6] = byte(ptr >> 48) + b[7] = byte(ptr >> 56) + } + h.Write(b) + return h.Sum32() +} + +func hashGobEncoder(e gob.GobEncoder) uint32 { + h := getHash() + b, err := e.GobEncode() + if err != nil { + panic(RT.NewError(err.Error())) + } + h.Write(b) + return h.Sum32() +} + +func (d *Delay) Hash() uint32 { + return hashPtr(uintptr(unsafe.Pointer(d))) +} + func (d *Delay) Force() Object { if d.value == nil { d.value = d.fn.Call([]Object{}) @@ -280,6 +323,10 @@ func (t *Type) GetType() *Type { return TYPES["Type"] } +func (t *Type) Hash() uint32 { + return hashPtr(uintptr(unsafe.Pointer(t))) +} + func (rb RecurBindings) ToString(escape bool) string { return "#object[RecurBindings]" } @@ -296,6 +343,10 @@ func (rb RecurBindings) GetType() *Type { return TYPES["RecurBindings"] } +func (t *RecurBindings) Hash() uint32 { + return 0 +} + func (exInfo *ExInfo) ToString(escape bool) string { return exInfo.msg.ToString(escape) } @@ -305,18 +356,17 @@ func (exInfo *ExInfo) Type() Symbol { } func (exInfo *ExInfo) Equals(other interface{}) bool { - switch other := other.(type) { - case *ExInfo: - return exInfo.msg == other.msg && exInfo.data.Equals(other.data) - default: - return false - } + return exInfo == other } func (exInfo *ExInfo) GetType() *Type { return TYPES["ExInfo"] } +func (exInfo *ExInfo) Hash() uint32 { + return hashPtr(uintptr(unsafe.Pointer(exInfo))) +} + func (exInfo *ExInfo) Error() string { var pos Position ok, form := exInfo.data.Get(MakeKeyword("form")) @@ -355,6 +405,10 @@ func (fn *Fn) GetType() *Type { return TYPES["Fn"] } +func (fn *Fn) Hash() uint32 { + return hashPtr(uintptr(unsafe.Pointer(fn))) +} + func (fn *Fn) Call(args []Object) Object { for _, arity := range fn.fnExpr.arities { if len(arity.args) == len(args) { @@ -413,12 +467,7 @@ func (p Proc) ToString(escape bool) string { } func (p Proc) Equals(other interface{}) bool { - switch other := other.(type) { - case Proc: - return &p == &other - default: - return false - } + return reflect.ValueOf(p).Pointer() == reflect.ValueOf(other).Pointer() } func (p Proc) GetInfo() *ObjectInfo { @@ -433,6 +482,10 @@ func (p Proc) GetType() *Type { return TYPES["Proc"] } +func (p Proc) Hash() uint32 { + return hashPtr(reflect.ValueOf(p).Pointer()) +} + func (i InfoHolder) GetInfo() *ObjectInfo { return i.info } @@ -447,6 +500,15 @@ func (sym Symbol) WithMeta(meta *ArrayMap) Object { return res } +func (v *Var) ToString(escape bool) string { + return "#'" + v.ns.Name.ToString(false) + "/" + v.name.ToString(false) +} + +func (v *Var) Equals(other interface{}) bool { + // TODO: revisit this + return v == other +} + func (v *Var) WithMeta(meta *ArrayMap) Object { res := *v res.meta = SafeMerge(res.meta, meta) @@ -457,6 +519,10 @@ func (v *Var) GetType() *Type { return TYPES["Var"] } +func (v *Var) Hash() uint32 { + return hashPtr(uintptr(unsafe.Pointer(v))) +} + func (v *Var) Call(args []Object) Object { return AssertCallable( v.Value, @@ -491,15 +557,6 @@ func MakeKeyword(nsname string) Keyword { } } -func (v *Var) ToString(escape bool) string { - return "#'" + v.ns.Name.ToString(false) + "/" + v.name.ToString(false) -} - -func (v *Var) Equals(other interface{}) bool { - // TODO: revisit this - return v == other -} - func (n Nil) ToString(escape bool) string { return "nil" } @@ -517,6 +574,10 @@ func (n Nil) GetType() *Type { return TYPES["Nil"] } +func (n Nil) Hash() uint32 { + return 0 +} + func (n Nil) Seq() Seq { return n } @@ -596,6 +657,10 @@ func (rat *Ratio) GetType() *Type { return TYPES["Ratio"] } +func (rat *Ratio) Hash() uint32 { + return hashGobEncoder(&rat.r) +} + func (rat *Ratio) Compare(other Object) int { return CompareNumbers(rat, AssertNumber(other, "Cannot compare Ratio and "+other.GetType().ToString(false))) } @@ -622,6 +687,10 @@ func (bi *BigInt) GetType() *Type { return TYPES["BigInt"] } +func (bi *BigInt) Hash() uint32 { + return hashGobEncoder(&bi.b) +} + func (bi *BigInt) Compare(other Object) int { return CompareNumbers(bi, AssertNumber(other, "Cannot compare BigInt and "+other.GetType().ToString(false))) } @@ -648,6 +717,10 @@ func (bf *BigFloat) GetType() *Type { return TYPES["BigFloat"] } +func (bf *BigFloat) Hash() uint32 { + return hashGobEncoder(&bf.b) +} + func (bf *BigFloat) Compare(other Object) int { return CompareNumbers(bf, AssertNumber(other, "Cannot compare BigFloat and "+other.GetType().ToString(false))) } diff --git a/run.sh b/run.sh index 34e9d5f08..357d26cba 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -go generate ./... && go tool vet ./ && go build && ./joker $@ +go generate ./... && go tool vet -shift=false ./ && go build && ./joker $@