diff --git a/pkg/primitive/binary.go b/pkg/primitive/binary.go index b7a8aa0d..374622a2 100644 --- a/pkg/primitive/binary.go +++ b/pkg/primitive/binary.go @@ -42,6 +42,21 @@ func (o Binary) Kind() Kind { return KindBinary } +func (o Binary) Equal(v Object) bool { + if r, ok := v.(Binary); !ok { + return false + } else if r.Len() != o.Len() { + return false + } else { + for i := 0; i < o.Len(); i++ { + if o.Get(i) != r.Get(i) { + return false + } + } + return true + } +} + func (o Binary) Hash() uint32 { h := fnv.New32() h.Write([]byte{byte(KindBinary), 0}) diff --git a/pkg/primitive/binary_test.go b/pkg/primitive/binary_test.go index 3f48fd17..d8616577 100644 --- a/pkg/primitive/binary_test.go +++ b/pkg/primitive/binary_test.go @@ -13,12 +13,6 @@ func TestNewBinary(t *testing.T) { assert.Equal(t, []byte{0}, v.Interface()) } -func TestBinary_Hash(t *testing.T) { - assert.NotEqual(t, NewBinary([]byte{0}).Hash(), NewBinary([]byte{1}).Hash()) - assert.Equal(t, NewBinary(nil).Hash(), NewBinary(nil).Hash()) - assert.Equal(t, NewBinary([]byte{0}).Hash(), NewBinary([]byte{0}).Hash()) -} - func TestBinary_Get(t *testing.T) { v := NewBinary([]byte{0}) @@ -26,6 +20,23 @@ func TestBinary_Get(t *testing.T) { assert.Equal(t, byte(0), v.Get(0)) } +func TestBinary_Equal(t *testing.T) { + v1 := NewBinary([]byte{0}) + v2 := NewBinary([]byte{0}) + v3 := NewBinary([]byte{1}) + + assert.True(t, v1.Equal(v2)) + assert.True(t, v2.Equal(v1)) + assert.False(t, v1.Equal(v3)) + assert.False(t, v2.Equal(v3)) +} + +func TestBinary_Hash(t *testing.T) { + assert.NotEqual(t, NewBinary([]byte{0}).Hash(), NewBinary([]byte{1}).Hash()) + assert.Equal(t, NewBinary(nil).Hash(), NewBinary(nil).Hash()) + assert.Equal(t, NewBinary([]byte{0}).Hash(), NewBinary([]byte{0}).Hash()) +} + func TestBinary_Encode(t *testing.T) { e := NewBinaryEncoder() diff --git a/pkg/primitive/bool.go b/pkg/primitive/bool.go index a098dad6..5459dd61 100644 --- a/pkg/primitive/bool.go +++ b/pkg/primitive/bool.go @@ -34,6 +34,14 @@ func (o Bool) Kind() Kind { return KindBool } +func (o Bool) Equal(v Object) bool { + if r, ok := v.(Bool); !ok { + return false + } else { + return o.Bool() == r.Bool() + } +} + func (o Bool) Hash() uint32 { var v byte if o { diff --git a/pkg/primitive/bool_test.go b/pkg/primitive/bool_test.go index 643b4161..eb0d6c2b 100644 --- a/pkg/primitive/bool_test.go +++ b/pkg/primitive/bool_test.go @@ -13,6 +13,13 @@ func TestNewBool(t *testing.T) { assert.Equal(t, true, v.Interface()) } +func TestBool_Equal(t *testing.T) { + assert.True(t, TRUE.Equal(TRUE)) + assert.True(t, FALSE.Equal(FALSE)) + assert.False(t, TRUE.Equal(FALSE)) + assert.False(t, FALSE.Equal(TRUE)) +} + func TestBool_Hash(t *testing.T) { assert.NotEqual(t, TRUE.Hash(), FALSE.Hash()) assert.Equal(t, TRUE.Hash(), TRUE.Hash()) diff --git a/pkg/primitive/float.go b/pkg/primitive/float.go index 93f7585a..f8a8bbed 100644 --- a/pkg/primitive/float.go +++ b/pkg/primitive/float.go @@ -38,6 +38,20 @@ func (o Float32) Kind() Kind { return KindFloat32 } +func (o Float32) Equal(v Object) bool { + if r, ok := v.(Float); !ok { + if r, ok := v.(Integer); ok { + return o.Float() == float64(r.Int()) + } else if r, ok := v.(Uinteger); ok { + return o.Float() == float64(r.Uint()) + } else { + return false + } + } else { + return o.Float() == r.Float() + } +} + func (o Float32) Hash() uint32 { var buf [4]byte binary.BigEndian.PutUint32(buf[:], math.Float32bits(float32(o))) @@ -67,6 +81,20 @@ func (o Float64) Kind() Kind { return KindFloat64 } +func (o Float64) Equal(v Object) bool { + if r, ok := v.(Float); !ok { + if r, ok := v.(Integer); ok { + return o.Float() == float64(r.Int()) + } else if r, ok := v.(Uinteger); ok { + return o.Float() == float64(r.Uint()) + } else { + return false + } + } else { + return o.Float() == r.Float() + } +} + func (o Float64) Hash() uint32 { var buf [8]byte binary.BigEndian.PutUint64(buf[:], math.Float64bits(float64(o))) diff --git a/pkg/primitive/float_test.go b/pkg/primitive/float_test.go index 6de5373d..07813cfd 100644 --- a/pkg/primitive/float_test.go +++ b/pkg/primitive/float_test.go @@ -21,6 +21,20 @@ func TestNewFloat(t *testing.T) { }) } +func TestFloat_Equal(t *testing.T) { + t.Run("32", func(t *testing.T) { + assert.True(t, NewFloat32(0).Equal(NewFloat32(0))) + assert.True(t, NewFloat32(0).Equal(NewFloat64(0))) + assert.False(t, NewFloat32(0).Equal(NewFloat32(1))) + }) + + t.Run("64", func(t *testing.T) { + assert.True(t, NewFloat64(0).Equal(NewFloat64(0))) + assert.True(t, NewFloat64(0).Equal(NewFloat32(0))) + assert.False(t, NewFloat64(1).Equal(NewFloat64(0))) + }) +} + func TestFloat_Hash(t *testing.T) { t.Run("32", func(t *testing.T) { assert.NotEqual(t, NewFloat32(0).Hash(), NewFloat32(1).Hash()) diff --git a/pkg/primitive/int.go b/pkg/primitive/int.go index 804271a9..e4a76900 100644 --- a/pkg/primitive/int.go +++ b/pkg/primitive/int.go @@ -46,6 +46,20 @@ func (o Int) Kind() Kind { return KindInt } +func (o Int) Equal(v Object) bool { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return o.Int() == int64(r.Uint()) + } else if r, ok := v.(Float); ok { + return float64(o.Int()) == r.Float() + } else { + return false + } + } else { + return o.Int() == r.Int() + } +} + func (o Int) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -74,6 +88,20 @@ func (o Int8) Kind() Kind { return KindInt8 } +func (o Int8) Equal(v Object) bool { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return o.Int() == int64(r.Uint()) + } else if r, ok := v.(Float); ok { + return float64(o.Int()) == r.Float() + } else { + return false + } + } else { + return o.Int() == r.Int() + } +} + func (o Int8) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -102,6 +130,20 @@ func (o Int16) Kind() Kind { return KindInt16 } +func (o Int16) Equal(v Object) bool { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return o.Int() == int64(r.Uint()) + } else if r, ok := v.(Float); ok { + return float64(o.Int()) == r.Float() + } else { + return false + } + } else { + return o.Int() == r.Int() + } +} + func (o Int16) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -130,6 +172,20 @@ func (o Int32) Kind() Kind { return KindInt32 } +func (o Int32) Equal(v Object) bool { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return o.Int() == int64(r.Uint()) + } else if r, ok := v.(Float); ok { + return float64(o.Int()) == r.Float() + } else { + return false + } + } else { + return o.Int() == r.Int() + } +} + func (o Int32) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -158,6 +214,20 @@ func (o Int64) Kind() Kind { return KindInt64 } +func (o Int64) Equal(v Object) bool { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return o.Int() == int64(r.Uint()) + } else if r, ok := v.(Float); ok { + return float64(o.Int()) == r.Float() + } else { + return false + } + } else { + return o.Int() == r.Int() + } +} + func (o Int64) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) diff --git a/pkg/primitive/int_test.go b/pkg/primitive/int_test.go index 7bfc7b87..09302dbe 100644 --- a/pkg/primitive/int_test.go +++ b/pkg/primitive/int_test.go @@ -39,6 +39,34 @@ func TestNewInt(t *testing.T) { }) } +func TestInt_Equal(t *testing.T) { + t.Run("", func(t *testing.T) { + assert.True(t, NewInt(0).Equal(NewInt(0))) + assert.False(t, NewInt(0).Equal(NewInt(1))) + assert.True(t, NewInt(0).Equal(NewFloat32(0))) + }) + t.Run("8", func(t *testing.T) { + assert.True(t, NewInt8(0).Equal(NewInt(0))) + assert.False(t, NewInt8(0).Equal(NewInt(1))) + assert.True(t, NewInt8(0).Equal(NewFloat32(0))) + }) + t.Run("16", func(t *testing.T) { + assert.True(t, NewInt16(0).Equal(NewInt(0))) + assert.False(t, NewInt16(0).Equal(NewInt(1))) + assert.True(t, NewInt16(0).Equal(NewFloat32(0))) + }) + t.Run("32", func(t *testing.T) { + assert.True(t, NewInt32(0).Equal(NewInt(0))) + assert.False(t, NewInt32(0).Equal(NewInt(1))) + assert.True(t, NewInt32(0).Equal(NewFloat32(0))) + }) + t.Run("64", func(t *testing.T) { + assert.True(t, NewInt64(0).Equal(NewInt(0))) + assert.False(t, NewInt64(0).Equal(NewInt(1))) + assert.True(t, NewInt64(0).Equal(NewFloat32(0))) + }) +} + func TestInt_Hash(t *testing.T) { t.Run("", func(t *testing.T) { assert.NotEqual(t, NewInt(0).Hash(), NewInt(1).Hash()) diff --git a/pkg/primitive/map.go b/pkg/primitive/map.go index 8009f84a..1c913c0a 100644 --- a/pkg/primitive/map.go +++ b/pkg/primitive/map.go @@ -107,6 +107,38 @@ func (o *Map) Kind() Kind { return KindMap } +func (o *Map) Equal(v Object) bool { + if r, ok := v.(*Map); !ok { + return false + } else if o.Len() != r.Len() { + return false + } else { + keys1 := o.Keys() + keys2 := r.Keys() + + for i, k1 := range keys1 { + k2 := keys2[i] + if k1 != k2 { + return false + } + + v1, ok1 := o.Get(k1) + v2, ok2 := o.Get(k2) + if ok1 != ok2 { + return false + } + if !ok1 || !ok2 { + continue + } + if !v1.Equal(v2) { + return false + } + } + + return true + } +} + func (o *Map) Hash() uint32 { h := fnv.New32() h.Write([]byte{byte(KindMap), 0}) diff --git a/pkg/primitive/map_test.go b/pkg/primitive/map_test.go index fd599552..f50a74bf 100644 --- a/pkg/primitive/map_test.go +++ b/pkg/primitive/map_test.go @@ -17,6 +17,16 @@ func TestNewMap(t *testing.T) { assert.Equal(t, map[string]string{k1.String(): v1.String()}, o.Interface()) } +func TestMap_Equal(t *testing.T) { + k1 := NewString(faker.Word()) + k2 := NewString(faker.Word()) + v1 := NewString(faker.Word()) + v2 := NewString(faker.Word()) + + assert.True(t, NewMap(k1, v1).Equal(NewMap(k1, v1))) + assert.False(t, NewMap(k1, v1).Equal(NewMap(k2, v2))) +} + func TestMap_Hash(t *testing.T) { k1 := NewString(faker.Word()) k2 := NewString(faker.Word()) diff --git a/pkg/primitive/object.go b/pkg/primitive/object.go index 3939177c..cd2ce887 100644 --- a/pkg/primitive/object.go +++ b/pkg/primitive/object.go @@ -6,6 +6,7 @@ type ( // Object is an atomic type. Object interface { Kind() Kind + Equal(v Object) bool Hash() uint32 Interface() any } diff --git a/pkg/primitive/slice.go b/pkg/primitive/slice.go index 7faa9ba6..c11efcdc 100644 --- a/pkg/primitive/slice.go +++ b/pkg/primitive/slice.go @@ -80,6 +80,21 @@ func (o *Slice) Kind() Kind { return KindSlice } +func (o *Slice) Equal(v Object) bool { + if r, ok := v.(*Slice); !ok { + return false + } else if o.Len() != r.Len() { + return false + } else { + for i := 0; i < o.Len(); i++ { + if !o.Get(i).Equal(r.Get(i)) { + return false + } + } + return true + } +} + func (o *Slice) Hash() uint32 { h := fnv.New32() h.Write([]byte{byte(KindSlice), 0}) diff --git a/pkg/primitive/slice_test.go b/pkg/primitive/slice_test.go index a2f2bd0b..171e36c8 100644 --- a/pkg/primitive/slice_test.go +++ b/pkg/primitive/slice_test.go @@ -16,15 +16,6 @@ func TestNewSlice(t *testing.T) { assert.Equal(t, []string{v1.String()}, o.Interface()) } -func TestSlice_Hash(t *testing.T) { - v1 := NewString(faker.Word()) - v2 := NewString(faker.Word()) - - assert.NotEqual(t, NewSlice(v1, v2).Hash(), NewSlice(v2, v1).Hash()) - assert.Equal(t, NewSlice().Hash(), NewSlice().Hash()) - assert.Equal(t, NewSlice(v1, v2).Hash(), NewSlice(v1, v2).Hash()) -} - func TestSlice_GetAndSet(t *testing.T) { v1 := NewString(faker.Word()) v2 := NewString(faker.Word()) @@ -71,6 +62,23 @@ func TestSlice_Sub(t *testing.T) { assert.Equal(t, 1, o.Len()) } +func TestSlice_Equal(t *testing.T) { + v1 := NewString(faker.Word()) + v2 := NewString(faker.Word()) + + assert.True(t, NewSlice(v1, v2).Equal(NewSlice(v1, v2))) + assert.False(t, NewSlice(v1, v2).Equal(NewSlice(v2, v1))) +} + +func TestSlice_Hash(t *testing.T) { + v1 := NewString(faker.Word()) + v2 := NewString(faker.Word()) + + assert.NotEqual(t, NewSlice(v1, v2).Hash(), NewSlice(v2, v1).Hash()) + assert.Equal(t, NewSlice().Hash(), NewSlice().Hash()) + assert.Equal(t, NewSlice(v1, v2).Hash(), NewSlice(v1, v2).Hash()) +} + func TestSlice_Encode(t *testing.T) { e := NewSliceEncoder(NewStringEncoder()) diff --git a/pkg/primitive/string.go b/pkg/primitive/string.go index eb700178..41ab9e45 100644 --- a/pkg/primitive/string.go +++ b/pkg/primitive/string.go @@ -41,6 +41,14 @@ func (o String) Kind() Kind { return KindString } +func (o String) Equal(v Object) bool { + if r, ok := v.(String); !ok { + return false + } else { + return o.String() == r.String() + } +} + func (o String) Hash() uint32 { h := fnv.New32() h.Write([]byte{byte(KindString), 0}) diff --git a/pkg/primitive/string_test.go b/pkg/primitive/string_test.go index 10998c28..bf16e243 100644 --- a/pkg/primitive/string_test.go +++ b/pkg/primitive/string_test.go @@ -14,6 +14,18 @@ func TestNewString(t *testing.T) { assert.Equal(t, KindString, v.Kind()) assert.Equal(t, raw, v.Interface()) } +func TestString_Get(t *testing.T) { + v := NewString("A") + + assert.Equal(t, 1, v.Len()) + assert.Equal(t, rune('A'), v.Get(0)) +} + +func TestString_Equal(t *testing.T) { + assert.True(t, NewString("A").Equal(NewString("A"))) + assert.True(t, NewString("").Equal(NewString(""))) + assert.False(t, NewString("A").Equal(NewString(""))) +} func TestString_Hash(t *testing.T) { assert.NotEqual(t, NewString("A").Hash(), NewString("B").Hash()) @@ -21,12 +33,6 @@ func TestString_Hash(t *testing.T) { assert.Equal(t, NewString("A").Hash(), NewString("A").Hash()) } -func TestString_Get(t *testing.T) { - v := NewString("A") - - assert.Equal(t, 1, v.Len()) - assert.Equal(t, rune('A'), v.Get(0)) -} func TestString_Encode(t *testing.T) { e := NewStringEncoder() diff --git a/pkg/primitive/uint.go b/pkg/primitive/uint.go index 8a8b4517..e14b1207 100644 --- a/pkg/primitive/uint.go +++ b/pkg/primitive/uint.go @@ -46,6 +46,20 @@ func (o Uint) Kind() Kind { return KindUint } +func (o Uint) Equal(v Object) bool { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return int64(o.Uint()) == r.Int() + } else if r, ok := v.(Float); ok { + return float64(o.Uint()) == r.Float() + } else { + return false + } + } else { + return o.Uint() == r.Uint() + } +} + func (o Uint) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -74,6 +88,20 @@ func (o Uint8) Kind() Kind { return KindUint8 } +func (o Uint8) Equal(v Object) bool { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return int64(o.Uint()) == r.Int() + } else if r, ok := v.(Float); ok { + return float64(o.Uint()) == r.Float() + } else { + return false + } + } else { + return o.Uint() == r.Uint() + } +} + func (o Uint8) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -102,6 +130,20 @@ func (o Uint16) Kind() Kind { return KindUint16 } +func (o Uint16) Equal(v Object) bool { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return int64(o.Uint()) == r.Int() + } else if r, ok := v.(Float); ok { + return float64(o.Uint()) == r.Float() + } else { + return false + } + } else { + return o.Uint() == r.Uint() + } +} + func (o Uint16) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -130,6 +172,20 @@ func (o Uint32) Kind() Kind { return KindUint32 } +func (o Uint32) Equal(v Object) bool { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return int64(o.Uint()) == r.Int() + } else if r, ok := v.(Float); ok { + return float64(o.Uint()) == r.Float() + } else { + return false + } + } else { + return o.Uint() == r.Uint() + } +} + func (o Uint32) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) @@ -158,6 +214,20 @@ func (o Uint64) Kind() Kind { return KindUint64 } +func (o Uint64) Equal(v Object) bool { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return int64(o.Uint()) == r.Int() + } else if r, ok := v.(Float); ok { + return float64(o.Uint()) == r.Float() + } else { + return false + } + } else { + return o.Uint() == r.Uint() + } +} + func (o Uint64) Hash() uint32 { buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) diff --git a/pkg/primitive/uint_test.go b/pkg/primitive/uint_test.go index 163b4372..6d4b5d7f 100644 --- a/pkg/primitive/uint_test.go +++ b/pkg/primitive/uint_test.go @@ -39,6 +39,34 @@ func TestNewUint(t *testing.T) { }) } +func TestUint_Equal(t *testing.T) { + t.Run("", func(t *testing.T) { + assert.True(t, NewInt(0).Equal(NewUint(0))) + assert.False(t, NewUint(0).Equal(NewUint(1))) + assert.True(t, NewUint(0).Equal(NewFloat32(0))) + }) + t.Run("8", func(t *testing.T) { + assert.True(t, NewUint8(0).Equal(NewUint(0))) + assert.False(t, NewUint8(0).Equal(NewUint(1))) + assert.True(t, NewUint8(0).Equal(NewFloat32(0))) + }) + t.Run("16", func(t *testing.T) { + assert.True(t, NewUint16(0).Equal(NewUint(0))) + assert.False(t, NewUint16(0).Equal(NewUint(1))) + assert.True(t, NewUint16(0).Equal(NewFloat32(0))) + }) + t.Run("32", func(t *testing.T) { + assert.True(t, NewUint32(0).Equal(NewUint(0))) + assert.False(t, NewUint32(0).Equal(NewUint(1))) + assert.True(t, NewUint32(0).Equal(NewFloat32(0))) + }) + t.Run("64", func(t *testing.T) { + assert.True(t, NewUint64(0).Equal(NewUint(0))) + assert.False(t, NewUint64(0).Equal(NewUint(1))) + assert.True(t, NewUint64(0).Equal(NewFloat32(0))) + }) +} + func TestUint_Hash(t *testing.T) { t.Run("", func(t *testing.T) { assert.NotEqual(t, NewUint(0).Hash(), NewUint(1).Hash())