diff --git a/cmd/resource/scheme.go b/cmd/resource/scheme.go index 3e1d9cda..f1b0f53e 100644 --- a/cmd/resource/scheme.go +++ b/cmd/resource/scheme.go @@ -2,7 +2,7 @@ package resource import ( "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" "github.com/siyul-park/uniflow/pkg/primitive" "github.com/siyul-park/uniflow/pkg/scheme" ) diff --git a/cmd/uniflow/apply/cmd.go b/cmd/uniflow/apply/cmd.go index c1b28e17..8a18041f 100644 --- a/cmd/uniflow/apply/cmd.go +++ b/cmd/uniflow/apply/cmd.go @@ -5,9 +5,9 @@ import ( "io/fs" "github.com/oklog/ulid/v2" + "github.com/samber/lo" "github.com/siyul-park/uniflow/cmd/flag" "github.com/siyul-park/uniflow/cmd/resource" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/scheme" "github.com/siyul-park/uniflow/pkg/storage" @@ -86,7 +86,7 @@ func NewCmd(config Config) *cobra.Command { } for _, spec := range specs { - if util.IsZero(spec.GetID()) { + if spec.GetID() == (ulid.ULID{}) { if spec.GetName() != "" { exist, err := st.FindOne(ctx, storage.Where[string](scheme.KeyName).EQ(spec.GetName()).And(storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace()))) if err != nil { @@ -107,7 +107,7 @@ func NewCmd(config Config) *cobra.Command { } exists, err := st.FindMany(ctx, storage.Where[ulid.ULID](scheme.KeyID).IN(ids...), &database.FindOptions{ - Limit: util.Ptr[int](len(ids)), + Limit: lo.ToPtr[int](len(ids)), }) if err != nil { return err diff --git a/cmd/uniflow/start/cmd.go b/cmd/uniflow/start/cmd.go index c3ba645d..948d1118 100644 --- a/cmd/uniflow/start/cmd.go +++ b/cmd/uniflow/start/cmd.go @@ -7,9 +7,9 @@ import ( "os/signal" "syscall" + "github.com/samber/lo" "github.com/siyul-park/uniflow/cmd/flag" "github.com/siyul-park/uniflow/cmd/resource" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/hook" "github.com/siyul-park/uniflow/pkg/runtime" @@ -63,7 +63,7 @@ func NewCmd(config Config) *cobra.Command { } if specs, err := st.FindMany(ctx, filter, &database.FindOptions{ - Limit: util.Ptr[int](1), + Limit: lo.ToPtr[int](1), }); err != nil { return err } else if len(specs) == 0 { diff --git a/go.mod b/go.mod index 797d0efd..e7f50d8c 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,15 @@ go 1.21.0 require ( github.com/benbjohnson/immutable v0.4.3 github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d + github.com/emirpasic/gods v1.18.1 github.com/evanw/esbuild v0.19.7 github.com/go-faker/faker/v4 v4.2.0 github.com/iancoleman/strcase v0.3.0 github.com/lithammer/dedent v1.1.0 - github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/oklog/ulid/v2 v2.1.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 + github.com/samber/lo v1.38.1 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index 513d956a..8c949991 100644 --- a/go.sum +++ b/go.sum @@ -68,14 +68,14 @@ github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d h1:wi6jN5LVt/ljaBG4ue7 github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanw/esbuild v0.19.5 h1:9ildZqajUJzDAwNf9MyQsLh2RdDRKTq3kcyyzhE39us= -github.com/evanw/esbuild v0.19.5/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/evanw/esbuild v0.19.7 h1:bAzcG5A0ctW6VhqzkWYIY0PuZuUmvYJCualhBsXWIYU= github.com/evanw/esbuild v0.19.7/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= @@ -184,8 +184,6 @@ github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffkt github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -214,6 +212,8 @@ github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9c github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -248,8 +248,6 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/xiatechs/jsonata-go v1.7.0 h1:eum70CuOqGEf+KtB9D9A5Ri189DLJpOe5DAHaazHa4w= -github.com/xiatechs/jsonata-go v1.7.0/go.mod h1:qc/5uRtTKE5mil6PncK/ogxFQyhqlI6YnxvdyAz57Xw= github.com/xiatechs/jsonata-go v1.7.1 h1:QuH8UYylziVnXHZZshs/1JY1/NGYxIyvpk1kmGTkpgo= github.com/xiatechs/jsonata-go v1.7.1/go.mod h1:qc/5uRtTKE5mil6PncK/ogxFQyhqlI6YnxvdyAz57Xw= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= diff --git a/internal/pool/map.go b/internal/pool/map.go deleted file mode 100644 index 10c00349..00000000 --- a/internal/pool/map.go +++ /dev/null @@ -1,21 +0,0 @@ -package pool - -import "sync" - -var ( - mapPool = sync.Pool{New: func() any { - return &sync.Map{} - }} -) - -func GetMap() *sync.Map { - return mapPool.Get().(*sync.Map) -} - -func PutMap(v *sync.Map) { - v.Range(func(key, _ any) bool { - v.Delete(key) - return true - }) - mapPool.Put(v) -} diff --git a/internal/pool/map_test.go b/internal/pool/map_test.go deleted file mode 100644 index a4f368bf..00000000 --- a/internal/pool/map_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package pool - -import ( - "github.com/go-faker/faker/v4" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestGetMap(t *testing.T) { - m := GetMap() - assert.NotNil(t, m) -} - -func TestPutMap(t *testing.T) { - m := GetMap() - - m.Store(faker.UUIDHyphenated(), faker.UUIDHyphenated()) - - PutMap(m) - - count := 0 - m.Range(func(_, _ any) bool { - count += 1 - return true - }) - - assert.Equal(t, 0, count) -} diff --git a/internal/util/compare.go b/internal/util/compare.go deleted file mode 100644 index e4c10905..00000000 --- a/internal/util/compare.go +++ /dev/null @@ -1,109 +0,0 @@ -package util - -import ( - "math" - "reflect" -) - -func IsZero(v any) bool { - if IsNil(v) { - return true - } - return reflect.ValueOf(v).IsZero() -} - -func Equal(x any, y any) bool { - if IsNil(x) != IsNil(y) { - return false - } - c, ok := compare(reflect.ValueOf(x), reflect.ValueOf(y)) - if !ok { - if hash1, err := Hash(x); err == nil { - if hash2, err := Hash(y); err == nil { - return hash1 == hash2 - } - } - return reflect.DeepEqual(x, y) // Is unsafe compare - } - return c == 0 -} - -func Compare(x any, y any) int { - c, ok := compare(reflect.ValueOf(x), reflect.ValueOf(y)) - if !ok { - return 0 - } - return c -} - -func compare(x, y reflect.Value) (int, bool) { - x = rawValue(x) - y = rawValue(y) - - k1 := basicKind(x) - k2 := basicKind(y) - - if k1 == invalidKind || k2 == invalidKind { - return 0, false - } - if k1 == pointerKind { - return compare(x.Elem(), y) - } - if k2 == pointerKind { - return compare(x, y.Elem()) - } - - if k1 != k2 { - switch { - case k1 == intKind && k2 == uintKind: - if x.Int() < 0 { - return -1, true - } - return compareStrict(uint64(x.Int()), y.Uint()), true - case k1 == uintKind && k2 == intKind: - if y.Int() < 0 { - return 1, true - } - return compareStrict(x.Uint(), uint64(y.Int())), true - default: - return compareStrict(k1, k2), true - } - } else { - switch k1 { - case nullKind: - return 0, true - case floatKind: - return compareStrict(x.Float(), y.Float()), true - case intKind: - return compareStrict(x.Int(), y.Int()), true - case uintKind: - return compareStrict(x.Uint(), y.Uint()), true - case stringKind: - return compareStrict(x.String(), y.String()), true - case iterableKind: - for i := 0; i < int(math.Min(float64(x.Len()), float64(y.Len()))); i++ { - if c, ok := compare(x.Index(i), y.Index(i)); ok && c != 0 { - return c, true - } else if !ok { - return 0, false - } - } - return compareStrict(x.Len(), y.Len()), true - default: - return 0, false - } - } -} - -func compareStrict[T Ordered](x T, y T) int { - if x == y { - return 0 - } - if x > y { - return 1 - } - if x < y { - return -1 - } - return 0 -} diff --git a/internal/util/compare_test.go b/internal/util/compare_test.go deleted file mode 100644 index 4427af82..00000000 --- a/internal/util/compare_test.go +++ /dev/null @@ -1,494 +0,0 @@ -package util - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIsZero(t *testing.T) { - var testCase = []struct { - when any - expect bool - }{ - { - when: nil, - expect: true, - }, - { - when: "", - expect: true, - }, - { - when: 0, - expect: true, - }, - { - when: false, - expect: true, - }, - { - when: struct{}{}, - expect: true, - }, - } - - for _, tc := range testCase { - assert.Equal(t, tc.expect, IsZero(tc.when)) - } -} - -func TestEqual(t *testing.T) { - var testCase = []struct { - when []any - expect bool - }{ - { - when: []any{uint8(0), uint8(0)}, - expect: true, - }, - { - when: []any{uint16(0), uint16(0)}, - expect: true, - }, - { - when: []any{uint32(0), uint32(0)}, - expect: true, - }, - { - when: []any{uint64(0), uint64(0)}, - expect: true, - }, - { - when: []any{int8(0), int8(0)}, - expect: true, - }, - { - when: []any{int16(0), int16(0)}, - expect: true, - }, - { - when: []any{int32(0), int32(0)}, - expect: true, - }, - { - when: []any{int64(0), int64(0)}, - expect: true, - }, - { - when: []any{int8(0), uint8(0)}, - expect: true, - }, - { - when: []any{int16(0), uint16(0)}, - expect: true, - }, - { - when: []any{int32(0), uint32(0)}, - expect: true, - }, - { - when: []any{int64(0), uint64(0)}, - expect: true, - }, - - { - when: []any{0, 1}, - expect: false, - }, - { - when: []any{false, true}, - expect: false, - }, - { - when: []any{"0", "1"}, - expect: false, - }, - } - - for _, tc := range testCase { - r := Equal(tc.when[0], tc.when[1]) - assert.Equal(t, tc.expect, r) - } -} - -func TestCompare(t *testing.T) { - var testCase1 = []struct { - when []any - expect int - }{ - { - when: []any{uint8(0), uint8(0)}, - expect: 0, - }, - { - when: []any{uint8(1), uint8(0)}, - expect: 1, - }, - { - when: []any{uint8(0), uint8(1)}, - expect: -1, - }, - { - when: []any{uint16(0), uint16(0)}, - expect: 0, - }, - { - when: []any{uint16(1), uint16(0)}, - expect: 1, - }, - { - when: []any{uint16(0), uint16(1)}, - expect: -1, - }, - { - when: []any{uint32(0), uint32(0)}, - expect: 0, - }, - { - when: []any{uint32(1), uint32(0)}, - expect: 1, - }, - { - when: []any{uint32(0), uint32(1)}, - expect: -1, - }, - { - when: []any{uint64(0), uint64(0)}, - expect: 0, - }, - { - when: []any{uint64(1), uint64(0)}, - expect: 1, - }, - { - when: []any{uint64(0), uint64(1)}, - expect: -1, - }, - { - when: []any{int8(0), int8(0)}, - expect: 0, - }, - { - when: []any{int8(1), int8(0)}, - expect: 1, - }, - { - when: []any{int8(0), int8(1)}, - expect: -1, - }, - { - when: []any{int16(0), int16(0)}, - expect: 0, - }, - { - when: []any{int16(1), int16(0)}, - expect: 1, - }, - { - when: []any{int16(0), int16(1)}, - expect: -1, - }, - { - when: []any{int32(0), int32(0)}, - expect: 0, - }, - { - when: []any{int32(1), int32(0)}, - expect: 1, - }, - { - when: []any{int32(0), int32(1)}, - expect: -1, - }, - { - when: []any{int64(0), int64(0)}, - expect: 0, - }, - { - when: []any{int64(1), int64(0)}, - expect: 1, - }, - { - when: []any{int64(0), int64(1)}, - expect: -1, - }, - { - when: []any{float32(0), float32(0)}, - expect: 0, - }, - { - when: []any{float32(1), float32(0)}, - expect: 1, - }, - { - when: []any{float32(0), float32(1)}, - expect: -1, - }, - { - when: []any{float64(0), float64(0)}, - expect: 0, - }, - { - when: []any{float64(1), float64(0)}, - expect: 1, - }, - { - when: []any{float64(0), float64(1)}, - expect: -1, - }, - { - when: []any{"0", "0"}, - expect: 0, - }, - { - when: []any{"1", "0"}, - expect: 1, - }, - { - when: []any{"0", "1"}, - expect: -1, - }, - { - when: []any{0, 0}, - expect: 0, - }, - { - when: []any{1, 0}, - expect: 1, - }, - { - when: []any{0, 1}, - expect: -1, - }, - { - when: []any{uint(0), uint(0)}, - expect: 0, - }, - { - when: []any{uint(1), uint(0)}, - expect: 1, - }, - { - when: []any{uint(0), uint(1)}, - expect: -1, - }, - { - when: []any{uintptr(0), uintptr(0)}, - expect: 0, - }, - { - when: []any{uintptr(1), uintptr(0)}, - expect: 1, - }, - { - when: []any{uintptr(0), uintptr(1)}, - expect: -1, - }, - { - when: []any{nil, 0}, - expect: -1, - }, - { - when: []any{0, nil}, - expect: 1, - }, - { - when: []any{nil, nil}, - expect: 0, - }, - } - - for _, tc := range testCase1 { - r := Compare(tc.when[0], tc.when[1]) - assert.Equal(t, tc.expect, r) - } - - var testCase2 = []struct { - whenX any - whenY any - expect int - ok bool - }{ - { - whenX: []uint8{uint8(0), uint8(0)}, - whenY: []uint8{uint8(0), uint8(0)}, - expect: 0, - }, - { - whenX: []uint8{uint8(0), uint8(1)}, - whenY: []uint8{uint8(0), uint8(0)}, - expect: 1, - }, - { - whenX: []uint8{uint8(0), uint8(1)}, - whenY: []uint8{uint8(1), uint8(0)}, - expect: -1, - }, - - { - whenX: []uint16{uint16(0), uint16(0)}, - whenY: []uint16{uint16(0), uint16(0)}, - expect: 0, - }, - { - whenX: []uint16{uint16(0), uint16(1)}, - whenY: []uint16{uint16(0), uint16(0)}, - expect: 1, - }, - { - whenX: []uint16{uint16(0), uint16(1)}, - whenY: []uint16{uint16(1), uint16(0)}, - expect: -1, - }, - - { - whenX: []uint32{uint32(0), uint32(0)}, - whenY: []uint32{uint32(0), uint32(0)}, - expect: 0, - }, - { - whenX: []uint32{uint32(0), uint32(1)}, - whenY: []uint32{uint32(0), uint32(0)}, - expect: 1, - }, - { - whenX: []uint32{uint32(0), uint32(1)}, - whenY: []uint32{uint32(1), uint32(0)}, - expect: -1, - }, - - { - whenX: []uint64{uint64(0), uint64(0)}, - whenY: []uint64{uint64(0), uint64(0)}, - expect: 0, - }, - { - whenX: []uint64{uint64(0), uint64(1)}, - whenY: []uint64{uint64(0), uint64(0)}, - expect: 1, - }, - { - whenX: []uint64{uint64(0), uint64(1)}, - whenY: []uint64{uint64(1), uint64(0)}, - expect: -1, - }, - - { - whenX: []int8{int8(0), int8(0)}, - whenY: []int8{int8(0), int8(0)}, - expect: 0, - }, - { - whenX: []int8{int8(0), int8(1)}, - whenY: []int8{int8(0), int8(0)}, - expect: 1, - }, - { - whenX: []int8{int8(0), int8(1)}, - whenY: []int8{int8(1), int8(0)}, - expect: -1, - }, - - { - whenX: []int16{int16(0), int16(0)}, - whenY: []int16{int16(0), int16(0)}, - expect: 0, - }, - { - whenX: []int16{int16(0), int16(1)}, - whenY: []int16{int16(0), int16(0)}, - expect: 1, - }, - { - whenX: []int16{int16(0), int16(1)}, - whenY: []int16{int16(1), int16(0)}, - expect: -1, - }, - - { - whenX: []int32{int32(0), int32(0)}, - whenY: []int32{int32(0), int32(0)}, - expect: 0, - }, - { - whenX: []int32{int32(0), int32(1)}, - whenY: []int32{int32(0), int32(0)}, - expect: 1, - }, - { - whenX: []int32{int32(0), int32(1)}, - whenY: []int32{int32(1), int32(0)}, - expect: -1, - }, - - { - whenX: []int64{int64(0), int64(0)}, - whenY: []int64{int64(0), int64(0)}, - expect: 0, - }, - { - whenX: []int64{int64(0), int64(1)}, - whenY: []int64{int64(0), int64(0)}, - expect: 1, - }, - { - whenX: []int64{int64(0), int64(1)}, - whenY: []int64{int64(1), int64(0)}, - expect: -1, - }, - - { - whenX: []float32{float32(0), float32(0)}, - whenY: []float32{float32(0), float32(0)}, - expect: 0, - }, - { - whenX: []float32{float32(0), float32(1)}, - whenY: []float32{float32(0), float32(0)}, - expect: 1, - }, - { - whenX: []float32{float32(0), float32(1)}, - whenY: []float32{float32(1), float32(0)}, - expect: -1, - }, - - { - whenX: []float64{float64(0), float64(0)}, - whenY: []float64{float64(0), float64(0)}, - expect: 0, - }, - { - whenX: []float64{float64(0), float64(1)}, - whenY: []float64{float64(0), float64(0)}, - expect: 1, - }, - { - whenX: []float64{float64(0), float64(1)}, - whenY: []float64{float64(1), float64(0)}, - expect: -1, - }, - - { - whenX: []string{"0", "0"}, - whenY: []string{"0", "0"}, - expect: 0, - }, - { - whenX: []string{"0", "1"}, - whenY: []string{"0", "0"}, - expect: 1, - }, - { - whenX: []string{"0", "1"}, - whenY: []string{"1", "0"}, - expect: -1, - }, - } - - for _, tc := range testCase2 { - r := Compare(tc.whenX, tc.whenY) - assert.Equal(t, tc.expect, r) - } -} diff --git a/internal/util/copy.go b/internal/util/copy.go deleted file mode 100644 index 50058965..00000000 --- a/internal/util/copy.go +++ /dev/null @@ -1,27 +0,0 @@ -package util - -import ( - "bytes" - "encoding/gob" -) - -func Copy[V any](source V) V { - if IsNil(source) { - return source - } - - var target V - - var buffer bytes.Buffer - encoder := gob.NewEncoder(&buffer) - decoder := gob.NewDecoder(&buffer) - - if err := encoder.Encode(source); err != nil { - return source - } - if err := decoder.Decode(&target); err != nil { - return source - } - - return target -} diff --git a/internal/util/copy_test.go b/internal/util/copy_test.go deleted file mode 100644 index f9f58997..00000000 --- a/internal/util/copy_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package util - -import ( - "fmt" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestCopy(t *testing.T) { - testCases := []struct { - when any - }{ - { - when: "string", - }, - { - when: 1, - }, - { - when: true, - }, - { - when: []any{"string", 1, true}, - }, - { - when: map[string]any{ - "string": "string", - "int": 1, - "bool": true, - "arr": []any{"string", 1, true}, - }, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("%v", tc.when), func(t *testing.T) { - assert.Equal(t, tc.when, Copy(tc.when)) - }) - } -} diff --git a/internal/util/hash.go b/internal/util/hash.go deleted file mode 100644 index 546ffb63..00000000 --- a/internal/util/hash.go +++ /dev/null @@ -1,7 +0,0 @@ -package util - -import "github.com/mitchellh/hashstructure/v2" - -func Hash(val any) (uint64, error) { - return hashstructure.Hash(val, hashstructure.FormatV2, nil) -} diff --git a/internal/util/kind.go b/internal/util/kind.go deleted file mode 100644 index 662849aa..00000000 --- a/internal/util/kind.go +++ /dev/null @@ -1,81 +0,0 @@ -package util - -import ( - "reflect" -) - -type ( - Ordered interface { - Integer | Float | ~string - } - Complex interface { - ~complex64 | ~complex128 - } - Float interface { - ~float32 | ~float64 - } - Integer interface { - Signed | Unsigned - } - Signed interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 - } - Unsigned interface { - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr - } -) - -type basisKind int - -const ( - invalidKind basisKind = iota - nullKind - intKind - uintKind - floatKind - complexKind - stringKind - mapKind - structKind - iterableKind - boolKind - pointerKind -) - -func basicKind(v reflect.Value) basisKind { - if !v.IsValid() || IsNil(v.Interface()) { - return nullKind - } - - switch v.Kind() { - case reflect.Bool: - return boolKind - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intKind - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return uintKind - case reflect.Float32, reflect.Float64: - return floatKind - case reflect.Complex64, reflect.Complex128: - return complexKind - case reflect.String: - return stringKind - case reflect.Map: - return mapKind - case reflect.Struct: - return structKind - case reflect.Slice, reflect.Array: - return iterableKind - case reflect.Pointer: - return pointerKind - } - return invalidKind -} - -func rawValue(x reflect.Value) reflect.Value { - if !x.IsValid() { - return x - } - - return reflect.ValueOf(x.Interface()) -} diff --git a/internal/util/ptr.go b/internal/util/ptr.go deleted file mode 100644 index fea833af..00000000 --- a/internal/util/ptr.go +++ /dev/null @@ -1,29 +0,0 @@ -package util - -import ( - "reflect" -) - -func IsNil(i any) bool { - defer func() { _ = recover() }() - return i == nil || reflect.ValueOf(i).IsNil() -} - -func Ptr[T any](value T) *T { - return &value -} - -func UnPtr[T any](value *T) T { - if !IsNil(value) { - return *value - } - var zero T - return zero -} - -func PtrTo[S any, T any](value *S, convert func(S) T) *T { - if IsNil(value) { - return nil - } - return Ptr(convert(UnPtr(value))) -} diff --git a/internal/util/ptr_test.go b/internal/util/ptr_test.go deleted file mode 100644 index b0e4ef50..00000000 --- a/internal/util/ptr_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package util - -import ( - "github.com/go-faker/faker/v4" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestIsNil(t *testing.T) { - assert.True(t, IsNil(nil)) - assert.False(t, IsNil(1)) - - type animal interface{} - type dog struct{} - - assert.False(t, IsNil(dog{})) - - var d *dog = nil - var a animal = d - assert.True(t, IsNil(a)) - assert.Nil(t, d) -} - -func TestPtr(t *testing.T) { - value := faker.UUIDHyphenated() - assert.Equal(t, value, *Ptr(value)) -} - -func TestUnPtr(t *testing.T) { - var nilPtr *string - assert.Equal(t, "", UnPtr(nilPtr)) - - value := faker.UUIDHyphenated() - ptr := &value - assert.Equal(t, value, UnPtr(ptr)) -} - -func TestPtrTo(t *testing.T) { - assert.Nil(t, PtrTo[int, int](nil, func(s int) int { return s + 1 })) - assert.Equal(t, UnPtr(PtrTo[int, int](Ptr(1), func(s int) int { return s + 1 })), 2) -} diff --git a/pkg/database/collection_test.go b/pkg/database/collection_test.go index 315a9b2a..3516e5db 100644 --- a/pkg/database/collection_test.go +++ b/pkg/database/collection_test.go @@ -3,43 +3,43 @@ package database import ( "testing" - "github.com/siyul-park/uniflow/internal/util" + "github.com/samber/lo" "github.com/stretchr/testify/assert" ) func TestMergeUpdateOptions(t *testing.T) { opt := MergeUpdateOptions([]*UpdateOptions{ nil, - util.Ptr(UpdateOptions{ + lo.ToPtr(UpdateOptions{ Upsert: nil, }), - util.Ptr(UpdateOptions{ - Upsert: util.Ptr(true), + lo.ToPtr(UpdateOptions{ + Upsert: lo.ToPtr(true), }), }) - assert.Equal(t, util.Ptr(UpdateOptions{ - Upsert: util.Ptr(true), + assert.Equal(t, lo.ToPtr(UpdateOptions{ + Upsert: lo.ToPtr(true), }), opt) } func TestMergeFindOptions(t *testing.T) { opt := MergeFindOptions([]*FindOptions{ nil, - util.Ptr(FindOptions{ - Limit: util.Ptr(1), + lo.ToPtr(FindOptions{ + Limit: lo.ToPtr(1), }), - util.Ptr(FindOptions{ - Skip: util.Ptr(1), + lo.ToPtr(FindOptions{ + Skip: lo.ToPtr(1), }), - util.Ptr(FindOptions{ + lo.ToPtr(FindOptions{ Sorts: []Sort{{Key: "", Order: OrderASC}}, }), }) - assert.Equal(t, util.Ptr(FindOptions{ - Limit: util.Ptr(1), - Skip: util.Ptr(1), + assert.Equal(t, lo.ToPtr(FindOptions{ + Limit: lo.ToPtr(1), + Skip: lo.ToPtr(1), Sorts: []Sort{{Key: "", Order: OrderASC}}, }), opt) } diff --git a/pkg/database/databasetest/collection.go b/pkg/database/databasetest/collection.go index 61932a0a..ac44bec3 100644 --- a/pkg/database/databasetest/collection.go +++ b/pkg/database/databasetest/collection.go @@ -7,7 +7,7 @@ import ( "github.com/go-faker/faker/v4" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" + "github.com/samber/lo" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" "github.com/stretchr/testify/assert" @@ -116,8 +116,8 @@ func AssertCollectionUpdateOne(t *testing.T, collection database.Collection) { primitive.NewString("version"), primitive.NewInt(0), ) - ok, err := collection.UpdateOne(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), util.Ptr(database.UpdateOptions{ - Upsert: util.Ptr(true), + ok, err := collection.UpdateOne(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), lo.ToPtr(database.UpdateOptions{ + Upsert: lo.ToPtr(true), })) assert.NoError(t, err) assert.True(t, ok) @@ -129,8 +129,8 @@ func AssertCollectionUpdateOne(t *testing.T, collection database.Collection) { primitive.NewString("version"), primitive.NewInt(0), ) - ok, err := collection.UpdateOne(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), util.Ptr(database.UpdateOptions{ - Upsert: util.Ptr(false), + ok, err := collection.UpdateOne(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), lo.ToPtr(database.UpdateOptions{ + Upsert: lo.ToPtr(false), })) assert.NoError(t, err) assert.False(t, ok) @@ -138,8 +138,8 @@ func AssertCollectionUpdateOne(t *testing.T, collection database.Collection) { _, err = collection.InsertOne(ctx, doc) assert.NoError(t, err) - ok, err = collection.UpdateOne(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), util.Ptr(database.UpdateOptions{ - Upsert: util.Ptr(false), + ok, err = collection.UpdateOne(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), lo.ToPtr(database.UpdateOptions{ + Upsert: lo.ToPtr(false), })) assert.NoError(t, err) assert.True(t, ok) @@ -158,8 +158,8 @@ func AssertCollectionUpdateMany(t *testing.T, collection database.Collection) { primitive.NewString("version"), primitive.NewInt(0), ) - count, err := collection.UpdateMany(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), util.Ptr(database.UpdateOptions{ - Upsert: util.Ptr(true), + count, err := collection.UpdateMany(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), lo.ToPtr(database.UpdateOptions{ + Upsert: lo.ToPtr(true), })) assert.NoError(t, err) assert.Equal(t, 1, count) @@ -171,8 +171,8 @@ func AssertCollectionUpdateMany(t *testing.T, collection database.Collection) { primitive.NewString("version"), primitive.NewInt(0), ) - count, err := collection.UpdateMany(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), util.Ptr(database.UpdateOptions{ - Upsert: util.Ptr(false), + count, err := collection.UpdateMany(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), lo.ToPtr(database.UpdateOptions{ + Upsert: lo.ToPtr(false), })) assert.NoError(t, err) assert.Equal(t, 0, count) @@ -180,8 +180,8 @@ func AssertCollectionUpdateMany(t *testing.T, collection database.Collection) { _, err = collection.InsertOne(ctx, doc) assert.NoError(t, err) - count, err = collection.UpdateMany(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), util.Ptr(database.UpdateOptions{ - Upsert: util.Ptr(false), + count, err = collection.UpdateMany(ctx, database.Where("id").EQ(doc.GetOr(primitive.NewString("id"), nil)), primitive.NewMap(primitive.NewString("version"), primitive.NewInt(1)), lo.ToPtr(database.UpdateOptions{ + Upsert: lo.ToPtr(false), })) assert.NoError(t, err) assert.Equal(t, 1, count) diff --git a/pkg/database/filter.go b/pkg/database/filter.go index 5fb2c18f..90c7dab1 100644 --- a/pkg/database/filter.go +++ b/pkg/database/filter.go @@ -168,7 +168,8 @@ func (ft *Filter) String() (string, error) { return ft.Key + " " + string(ft.OP), nil } - b, err := json.Marshal(primitive.Interface(ft.Value)) + v, _ := ft.Value.(primitive.Object) + b, err := json.Marshal(primitive.Interface(v)) if err != nil { return "", err } diff --git a/pkg/database/memdb/collection.go b/pkg/database/memdb/collection.go index ce9d6f98..517258c6 100644 --- a/pkg/database/memdb/collection.go +++ b/pkg/database/memdb/collection.go @@ -5,9 +5,11 @@ import ( "sort" "sync" + "github.com/emirpasic/gods/maps" + "github.com/emirpasic/gods/maps/treemap" + "github.com/emirpasic/gods/utils" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/pool" - "github.com/siyul-park/uniflow/internal/util" + "github.com/samber/lo" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" ) @@ -15,7 +17,7 @@ import ( type ( Collection struct { name string - data *sync.Map + data maps.Map indexView *IndexView streams []*Stream streamMatches []func(*primitive.Map) bool @@ -42,7 +44,7 @@ var ( func NewCollection(name string) *Collection { return &Collection{ name: name, - data: pool.GetMap(), + data: treemap.NewWith(comparator), indexView: NewIndexView(), dataLock: sync.RWMutex{}, streamLock: sync.RWMutex{}, @@ -120,7 +122,7 @@ func (coll *Collection) UpdateOne(ctx context.Context, filter *database.Filter, opt := database.MergeUpdateOptions(opts) upsert := false if opt != nil && opt.Upsert != nil { - upsert = util.UnPtr(opt.Upsert) + upsert = lo.FromPtr(opt.Upsert) } old, err := coll.findOne(ctx, filter) @@ -186,7 +188,7 @@ func (coll *Collection) UpdateMany(ctx context.Context, filter *database.Filter, opt := database.MergeUpdateOptions(opts) upsert := false if opt != nil && opt.Upsert != nil { - upsert = util.UnPtr(opt.Upsert) + upsert = lo.FromPtr(opt.Upsert) } old, err := coll.findMany(ctx, filter) @@ -301,12 +303,12 @@ func (coll *Collection) FindMany(ctx context.Context, filter *database.Filter, o } func (coll *Collection) Drop(ctx context.Context) error { - data, err := func() (*sync.Map, error) { + data, err := func() (maps.Map, error) { coll.dataLock.Lock() defer coll.dataLock.Unlock() data := coll.data - coll.data = pool.GetMap() + coll.data = treemap.NewWith(comparator) if err := coll.indexView.deleteAll(ctx); err != nil { return nil, err @@ -318,7 +320,7 @@ func (coll *Collection) Drop(ctx context.Context) error { return err } - data.Range(func(_, val any) bool { + for _, val := range data.Values() { doc := val.(*primitive.Map) if id, ok := doc.Get(keyID); ok { coll.emit(fullEvent{ @@ -329,8 +331,7 @@ func (coll *Collection) Drop(ctx context.Context) error { Document: doc, }) } - return true - }) + } coll.streamLock.Lock() defer coll.streamLock.Unlock() @@ -361,9 +362,7 @@ func (coll *Collection) insertMany(ctx context.Context, docs []*primitive.Map) ( for i, doc := range docs { if id, ok := doc.Get(keyID); !ok { return nil, errors.Wrap(errors.WithStack(ErrPKNotFound), database.ErrCodeWrite) - } else if hash, err := util.Hash(id); err != nil { - return nil, errors.Wrap(err, database.ErrCodeWrite) - } else if _, ok := coll.data.Load(hash); ok { + } else if _, ok := coll.data.Get(id); ok { return nil, errors.Wrap(errors.WithStack(ErrPKDuplicated), database.ErrCodeWrite) } else { ids[i] = id @@ -374,18 +373,14 @@ func (coll *Collection) insertMany(ctx context.Context, docs []*primitive.Map) ( return nil, errors.Wrap(err, database.ErrCodeWrite) } for i, doc := range docs { - if hash, err := util.Hash(ids[i].Interface()); err != nil { - return nil, errors.Wrap(err, database.ErrCodeWrite) - } else { - coll.data.Store(hash, doc) - } + coll.data.Put(ids[i], doc) } return ids, nil } func (coll *Collection) findOne(ctx context.Context, filter *database.Filter, opts ...*database.FindOptions) (*primitive.Map, error) { - opt := database.MergeFindOptions(append(opts, util.Ptr(database.FindOptions{Limit: util.Ptr(1)}))) + opt := database.MergeFindOptions(append(opts, lo.ToPtr(database.FindOptions{Limit: lo.ToPtr(1)}))) if docs, err := coll.findMany(ctx, filter, opt); err != nil { return nil, err @@ -404,11 +399,11 @@ func (coll *Collection) findMany(ctx context.Context, filter *database.Filter, o limit := -1 if opt != nil && opt.Limit != nil { - limit = util.UnPtr(opt.Limit) + limit = lo.FromPtr(opt.Limit) } skip := 0 if opt != nil && opt.Skip != nil { - skip = util.UnPtr(opt.Skip) + skip = lo.FromPtr(opt.Skip) } var sorts []database.Sort if opt != nil && opt.Sorts != nil { @@ -422,40 +417,39 @@ func (coll *Collection) findMany(ctx context.Context, filter *database.Filter, o scanSize = -1 } - scan := map[uint64]*primitive.Map{} + scan := treemap.NewWith(utils.Comparator(func(a, b any) int { + return primitive.Compare(a.(primitive.Object), b.(primitive.Object)) + })) if examples, ok := FilterToExample(filter); ok { if ids, err := coll.indexView.findMany(ctx, examples); err == nil { for _, id := range ids { - if scanSize == len(scan) { + if scanSize == scan.Size() { break - } else if hash, err := util.Hash(id.Interface()); err != nil { - return nil, errors.Wrap(err, database.ErrCodeWrite) - } else if doc, ok := coll.data.Load(hash); ok && match(doc.(*primitive.Map)) { - scan[hash] = doc.(*primitive.Map) + } else if doc, ok := coll.data.Get(id); ok && match(doc.(*primitive.Map)) { + scan.Put(id, doc) } } } } - if scanSize != len(scan) { - coll.data.Range(func(key, value any) bool { - if scanSize == len(scan) { - return false + if scanSize != scan.Size() { + for _, key := range coll.data.Keys() { + value, _ := coll.data.Get(key) + if scanSize == scan.Size() { + continue } - if match(value.(*primitive.Map)) { - scan[key.(uint64)] = value.(*primitive.Map) + scan.Put(key, value) } - return true - }) + } } - if skip >= len(scan) { + if skip >= scan.Size() { return nil, nil } var docs []*primitive.Map - for _, doc := range scan { - docs = append(docs, doc) + for _, doc := range scan.Values() { + docs = append(docs, doc.(*primitive.Map)) } if len(sorts) > 0 { @@ -507,11 +501,7 @@ func (coll *Collection) deleteMany(ctx context.Context, docs []*primitive.Map) ( } for _, id := range ids { - if hash, err := util.Hash(id.Interface()); err != nil { - return nil, errors.Wrap(err, database.ErrCodeWrite) - } else { - coll.data.Delete(hash) - } + coll.data.Remove(id) } return deletes, nil diff --git a/pkg/database/memdb/compare.go b/pkg/database/memdb/compare.go new file mode 100644 index 00000000..7d68e0f2 --- /dev/null +++ b/pkg/database/memdb/compare.go @@ -0,0 +1,12 @@ +package memdb + +import ( + "github.com/emirpasic/gods/utils" + "github.com/siyul-park/uniflow/pkg/primitive" +) + +var ( + comparator = utils.Comparator(func(a, b any) int { + return primitive.Compare(a.(primitive.Object), b.(primitive.Object)) + }) +) diff --git a/pkg/database/memdb/filter.go b/pkg/database/memdb/filter.go index e0d995fa..9980e281 100644 --- a/pkg/database/memdb/filter.go +++ b/pkg/database/memdb/filter.go @@ -1,7 +1,6 @@ package memdb import ( - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" ) @@ -16,63 +15,75 @@ func ParseFilter(filter *database.Filter) func(*primitive.Map) bool { switch filter.OP { case database.EQ: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { + return false + } else if v, ok := filter.Value.(primitive.Object); !ok { return false } else { - return util.Equal(primitive.Interface(v), primitive.Interface(filter.Value)) + return primitive.Compare(o, v) == 0 } } case database.NE: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { + return false + } else if v, ok := filter.Value.(primitive.Object); !ok { return false } else { - return !util.Equal(primitive.Interface(v), primitive.Interface(filter.Value)) + return primitive.Compare(o, v) != 0 } } case database.LT: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { + return false + } else if v, ok := filter.Value.(primitive.Object); !ok { return false } else { - return util.Compare(primitive.Interface(v), primitive.Interface(filter.Value)) < 0 + return primitive.Compare(o, v) < 0 } } case database.LTE: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { + return false + } else if v, ok := filter.Value.(primitive.Object); !ok { return false } else { - return util.Compare(primitive.Interface(v), primitive.Interface(filter.Value)) <= 0 + return primitive.Compare(o, v) <= 0 } } case database.GT: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { + return false + } else if v, ok := filter.Value.(primitive.Object); !ok { return false } else { - return util.Compare(primitive.Interface(v), primitive.Interface(filter.Value)) > 0 + return primitive.Compare(o, v) > 0 } } case database.GTE: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { + return false + } else if v, ok := filter.Value.(primitive.Object); !ok { return false } else { - return util.Compare(primitive.Interface(v), primitive.Interface(filter.Value)) >= 0 + return primitive.Compare(o, v) >= 0 } } case database.IN: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { return false - } else if v == nil { + } else if o == nil { return false - } else if children, ok := filter.Value.(*primitive.Slice); !ok { + } else if v, ok := filter.Value.(*primitive.Slice); !ok { return false } else { - for i := 0; i < children.Len(); i++ { - if util.Equal(v.Interface(), children.Get(i).Interface()) { + for i := 0; i < v.Len(); i++ { + if primitive.Compare(o, v.Get(i)) == 0 { return true } } @@ -81,35 +92,35 @@ func ParseFilter(filter *database.Filter) func(*primitive.Map) bool { } case database.NIN: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { - return false - } else if v == nil { + if o, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { return true - } else if children, ok := filter.Value.(*primitive.Slice); !ok { + } else if o == nil { + return true + } else if v, ok := filter.Value.(*primitive.Slice); !ok { return false } else { - for i := 0; i < children.Len(); i++ { - if util.Equal(v.Interface(), children.Get(i).Interface()) { - return false + for i := 0; i < v.Len(); i++ { + if primitive.Compare(o, v.Get(i)) != 0 { + return true } } - return true + return false } } case database.NULL: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if v, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { return false } else { - return util.IsNil(v) + return v == nil } } case database.NNULL: return func(m *primitive.Map) bool { - if v, ok := primitive.Get[primitive.Object](m, filter.Key); !ok { + if v, ok := primitive.Pick[primitive.Object](m, filter.Key); !ok { return false } else { - return !util.IsNil(v) + return v != nil } } case database.AND: @@ -158,7 +169,7 @@ func ParseFilter(filter *database.Filter) func(*primitive.Map) bool { } func FilterToExample(filter *database.Filter) ([]*primitive.Map, bool) { - if util.IsNil(filter) { + if filter == nil { return nil, false } diff --git a/pkg/database/memdb/index.go b/pkg/database/memdb/index.go index 4ce3895c..8d0232f3 100644 --- a/pkg/database/memdb/index.go +++ b/pkg/database/memdb/index.go @@ -4,9 +4,12 @@ import ( "context" "sync" + "github.com/emirpasic/gods/containers" + "github.com/emirpasic/gods/maps" + "github.com/emirpasic/gods/maps/treemap" + "github.com/emirpasic/gods/sets" + "github.com/emirpasic/gods/sets/treeset" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/pool" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" ) @@ -15,7 +18,7 @@ type ( IndexView struct { names []string models []database.IndexModel - data []*sync.Map + data []maps.Map lock sync.RWMutex } ) @@ -72,7 +75,7 @@ func (iv *IndexView) Create(_ context.Context, index database.IndexModel) error iv.names = append(iv.names, name) iv.models = append(iv.models, index) - iv.data = append(iv.data, pool.GetMap()) + iv.data = append(iv.data, treemap.NewWith(comparator)) return nil } @@ -135,8 +138,7 @@ func (iv *IndexView) findMany(_ context.Context, examples []*primitive.Map) ([]p iv.lock.RLock() defer iv.lock.RUnlock() - ids := pool.GetMap() - defer pool.PutMap(ids) + ids := treeset.NewWith(comparator) for _, example := range examples { if err := func() error { @@ -156,30 +158,16 @@ func (iv *IndexView) findMany(_ context.Context, examples []*primitive.Map) ([]p var i int var k string for i, k = range model.Keys { - if obj, ok := primitive.Get[any](example, k); ok { - v := primitive.Interface(obj) - - hash, err := util.Hash(v) - if err != nil { - return err - } + if obj, ok := primitive.Pick[primitive.Object](example, k); ok { visits[k] = true - if sub, ok := curr.Load(hash); ok { + if sub, ok := curr.Get(obj); ok { if i < len(model.Keys)-1 { - curr = sub.(*sync.Map) + curr = sub.(maps.Map) } else { if model.Unique { - if hsub, err := util.Hash(sub); err != nil { - return err - } else { - ids.Store(hsub, sub) - return nil - } + ids.Add(sub) } else { - sub.(*sync.Map).Range(func(key, val any) bool { - ids.Store(key, val) - return true - }) + ids.Add(sub.(sets.Set).Values()...) return nil } } @@ -201,7 +189,7 @@ func (iv *IndexView) findMany(_ context.Context, examples []*primitive.Map) ([]p continue } - var parent []*sync.Map + var parent []maps.Map parent = append(parent, curr) depth := len(model.Keys) - 1 @@ -210,21 +198,17 @@ func (iv *IndexView) findMany(_ context.Context, examples []*primitive.Map) ([]p } for ; i < depth; i++ { - var children []*sync.Map + var children []maps.Map for _, curr := range parent { - curr.Range(func(_, value any) bool { - children = append(children, value.(*sync.Map)) - return true - }) + for _, v := range curr.Values() { + children = append(children, v.(maps.Map)) + } } parent = children } for _, curr := range parent { - curr.Range(func(k, v any) bool { - ids.Store(k, v) - return true - }) + ids.Add(curr.Values()...) } return nil @@ -237,10 +221,9 @@ func (iv *IndexView) findMany(_ context.Context, examples []*primitive.Map) ([]p } var uniqueIds []primitive.Object - ids.Range(func(_, val any) bool { - uniqueIds = append(uniqueIds, val.(primitive.Object)) - return true - }) + for _, v := range ids.Values() { + uniqueIds = append(uniqueIds, v.(primitive.Object)) + } return uniqueIds, nil } @@ -259,31 +242,28 @@ func (iv *IndexView) insertOne(ctx context.Context, doc *primitive.Map) error { } for i, k := range model.Keys { - obj, _ := primitive.Get[primitive.Object](doc, k) - v := primitive.Interface(obj) + obj, _ := primitive.Pick[primitive.Object](doc, k) - hash, err := util.Hash(v) - if err != nil { - return err - } if i < len(model.Keys)-1 { - cm := pool.GetMap() - sub, load := curr.LoadOrStore(hash, cm) - if load { - pool.PutMap(cm) + sub, ok := curr.Get(obj) + if !ok { + sub = treemap.NewWith(comparator) + curr.Put(obj, sub) } - curr = sub.(*sync.Map) + curr = sub.(maps.Map) } else if model.Unique { - if r, loaded := curr.LoadOrStore(hash, id); loaded && r != id { + if r, ok := curr.Get(obj); !ok { + curr.Put(obj, id) + } else if r != id { return ErrIndexConflict } } else { - cm := pool.GetMap() - r, load := curr.LoadOrStore(hash, cm) - if load { - pool.PutMap(cm) + r, ok := curr.Get(obj) + if !ok { + r = treeset.NewWith(comparator) + curr.Put(obj, r) } - r.(*sync.Map).Store(hash, id) + r.(sets.Set).Add(id) } } @@ -303,11 +283,6 @@ func (iv *IndexView) deleteOne(_ context.Context, doc *primitive.Map) error { return nil } - hid, err := util.Hash(id) - if err != nil { - return err - } - for i, model := range iv.models { if err := func() error { curr := iv.data[i] @@ -316,38 +291,32 @@ func (iv *IndexView) deleteOne(_ context.Context, doc *primitive.Map) error { return nil } - var nodes []*sync.Map + var nodes []containers.Container nodes = append(nodes, curr) - var keys []any + var keys []primitive.Object keys = append(keys, nil) for i, k := range model.Keys { - obj, _ := primitive.Get[primitive.Object](doc, k) - v := primitive.Interface(obj) - - hash, err := util.Hash(v) - if err != nil { - return err - } + obj, _ := primitive.Pick[primitive.Object](doc, k) if i < len(model.Keys)-1 { - if sub, ok := curr.Load(hash); ok { - curr = sub.(*sync.Map) + if sub, ok := curr.Get(obj); ok { + curr = sub.(maps.Map) nodes = append(nodes, curr) - keys = append(keys, hash) + keys = append(keys, obj) } else { return nil } } else if model.Unique { - if r, loaded := curr.Load(hash); loaded && util.Equal(r, id) { - curr.Delete(hash) + if r, ok := curr.Get(obj); ok && primitive.Compare(id, r.(primitive.Object)) == 0 { + curr.Remove(obj) } } else { - if r, loaded := curr.Load(hash); loaded { - nodes = append(nodes, r.(*sync.Map)) - keys = append(keys, hash) - r.(*sync.Map).Delete(hid) + if r, ok := curr.Get(obj); ok { + nodes = append(nodes, r.(sets.Set)) + keys = append(keys, obj) + r.(sets.Set).Remove(id) } } } @@ -355,18 +324,13 @@ func (iv *IndexView) deleteOne(_ context.Context, doc *primitive.Map) error { for i := len(nodes) - 1; i >= 0; i-- { node := nodes[i] - empty := true - node.Range(func(_, _ any) bool { - empty = false - return false - }) - - if empty && i > 0 { + if node.Empty() && i > 0 { parent := nodes[i-1] key := keys[i] - parent.Delete(key) - pool.PutMap(node) + if p, ok := parent.(maps.Map); ok { + p.Remove(key) + } } } diff --git a/pkg/database/memdb/sort.go b/pkg/database/memdb/sort.go index c1cc072f..fbafa7f0 100644 --- a/pkg/database/memdb/sort.go +++ b/pkg/database/memdb/sort.go @@ -1,7 +1,6 @@ package memdb import ( - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" ) @@ -9,8 +8,8 @@ import ( func ParseSorts(sorts []database.Sort) func(i, j *primitive.Map) bool { return func(i, j *primitive.Map) bool { for _, s := range sorts { - x, _ := primitive.Get[primitive.Object](i, s.Key) - y, _ := primitive.Get[primitive.Object](j, s.Key) + x, _ := primitive.Pick[primitive.Object](i, s.Key) + y, _ := primitive.Pick[primitive.Object](j, s.Key) if x == y { continue @@ -20,7 +19,7 @@ func ParseSorts(sorts []database.Sort) func(i, j *primitive.Map) bool { return s.Order != database.OrderDESC } - e := util.Compare(x.Interface(), y.Interface()) + e := primitive.Compare(x, y) if e == 0 { continue } diff --git a/pkg/database/mongodb/collection.go b/pkg/database/mongodb/collection.go index a896775e..63285f11 100644 --- a/pkg/database/mongodb/collection.go +++ b/pkg/database/mongodb/collection.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/util" + "github.com/samber/lo" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" "go.mongodb.org/mongo-driver/bson" @@ -235,7 +235,7 @@ func mongoUpdateOptions(opts *database.UpdateOptions) *options.UpdateOptions { if opts == nil { return nil } - return util.Ptr(options.UpdateOptions{ + return lo.ToPtr(options.UpdateOptions{ Upsert: opts.Upsert, }) } @@ -244,8 +244,8 @@ func mongoFindOneOptions(opts *database.FindOptions) *options.FindOneOptions { if opts == nil { return nil } - return util.Ptr(options.FindOneOptions{ - Skip: util.PtrTo(opts.Skip, func(s int) int64 { return int64(s) }), + return lo.ToPtr(options.FindOneOptions{ + Skip: lo.EmptyableToPtr(int64(lo.FromPtr(opts.Skip))), Sort: mongoSorts(opts.Sorts), }) } @@ -254,9 +254,9 @@ func mongoFindOptions(opts *database.FindOptions) *options.FindOptions { if opts == nil { return nil } - return util.Ptr(options.FindOptions{ - Limit: util.PtrTo(opts.Limit, func(s int) int64 { return int64(s) }), - Skip: util.PtrTo(opts.Skip, func(s int) int64 { return int64(s) }), + return lo.ToPtr(options.FindOptions{ + Limit: lo.EmptyableToPtr(int64(lo.FromPtr(opts.Limit))), + Skip: lo.EmptyableToPtr(int64(lo.FromPtr(opts.Skip))), Sort: mongoSorts(opts.Sorts), }) } diff --git a/pkg/database/mongodb/encoding.go b/pkg/database/mongodb/encoding.go index bffdb134..0d5e65c3 100644 --- a/pkg/database/mongodb/encoding.go +++ b/pkg/database/mongodb/encoding.go @@ -5,9 +5,8 @@ import ( "github.com/iancoleman/strcase" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" + "github.com/siyul-park/uniflow/pkg/encoding" "github.com/siyul-park/uniflow/pkg/primitive" "go.mongodb.org/mongo-driver/bson" bsonprimitive "go.mongodb.org/mongo-driver/bson/primitive" @@ -179,13 +178,13 @@ func NewFilterDecoder(decoder encoding.Decoder[any, *primitive.Object]) encoding Key: key, } if op == "$eq" { - if util.IsNil(v) { + if v == nil { child.OP = database.NULL } else { child.OP = database.EQ } } else if op == "$ne" { - if util.IsNil(v) { + if v == nil { child.OP = database.NNULL } else { child.OP = database.NE diff --git a/pkg/database/mongodb/index.go b/pkg/database/mongodb/index.go index f75eb12c..e380ba4e 100644 --- a/pkg/database/mongodb/index.go +++ b/pkg/database/mongodb/index.go @@ -3,7 +3,7 @@ package mongodb import ( "context" - "github.com/siyul-park/uniflow/internal/util" + "github.com/samber/lo" "github.com/siyul-park/uniflow/pkg/database" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -74,8 +74,8 @@ func (iv *IndexView) Create(ctx context.Context, index database.IndexModel) erro _, err = iv.raw.CreateOne(ctx, mongo.IndexModel{ Keys: keys, Options: &options.IndexOptions{ - Name: util.Ptr(index.Name), - Unique: util.Ptr(index.Unique), + Name: lo.ToPtr(index.Name), + Unique: lo.ToPtr(index.Unique), PartialFilterExpression: partialFilterExpression, }, }) diff --git a/internal/encoding/decoder.go b/pkg/encoding/decoder.go similarity index 100% rename from internal/encoding/decoder.go rename to pkg/encoding/decoder.go diff --git a/internal/encoding/encoder.go b/pkg/encoding/encoder.go similarity index 100% rename from internal/encoding/encoder.go rename to pkg/encoding/encoder.go diff --git a/internal/encoding/error.go b/pkg/encoding/error.go similarity index 100% rename from internal/encoding/error.go rename to pkg/encoding/error.go diff --git a/internal/encoding/group.go b/pkg/encoding/group.go similarity index 100% rename from internal/encoding/group.go rename to pkg/encoding/group.go diff --git a/internal/encoding/group_test.go b/pkg/encoding/group_test.go similarity index 100% rename from internal/encoding/group_test.go rename to pkg/encoding/group_test.go diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go index d11898cc..051730c0 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -2,10 +2,10 @@ package loader import ( "context" + "reflect" "sync" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database/memdb" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/scheme" @@ -103,7 +103,7 @@ func (ld *Loader) loadOne(ctx context.Context, filter *storage.Filter) (node.Nod if remote != nil { if local != nil { - if ok := util.Equal(local, remote); ok { + if reflect.DeepEqual(remote, local) { if n, ok := ld.table.Lookup(remote.GetID()); ok { return n, nil } @@ -178,7 +178,7 @@ func (ld *Loader) loadMany(ctx context.Context, filter *storage.Filter) ([]node. for id, remote := range idToRemote { local := idToLocal[id] if local != nil { - if ok := util.Equal(local, remote); ok { + if reflect.DeepEqual(remote, local) { if n, ok := ld.table.Lookup(id); ok { nodes = append(nodes, n) continue @@ -289,7 +289,7 @@ func (ld *Loader) resolveLinks(ctx context.Context, local scheme.Spec, remote sc for _, location := range locations { id := location.ID - if util.IsZero(id) { + if id == (ulid.ULID{}) { if location.Name != "" { filter := storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace()) filter = filter.And(storage.Where[string](scheme.KeyName).EQ(location.Name)) @@ -301,7 +301,7 @@ func (ld *Loader) resolveLinks(ctx context.Context, local scheme.Spec, remote sc } } - if !util.IsZero(id) { + if id != (ulid.ULID{}) { if ref, ok := ld.table.Lookup(id); ok { referenced := ld.referenced[ref.ID()] var locations []scheme.PortLocation @@ -331,7 +331,7 @@ func (ld *Loader) resolveLinks(ctx context.Context, local scheme.Spec, remote sc for _, location := range locations { filter := storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace()) - if !util.IsZero(location.ID) { + if location.ID != (ulid.ULID{}) { filter = filter.And(storage.Where[ulid.ULID](scheme.KeyID).EQ(location.ID)) } else if location.Name != "" { filter = filter.And(storage.Where[string](scheme.KeyName).EQ(location.Name)) @@ -410,7 +410,7 @@ func (ld *Loader) resolveLinks(ctx context.Context, local scheme.Spec, remote sc } for _, location := range locations { - if (!util.IsZero(location.ID) && location.ID == spec.GetID()) || (location.Name != "" && location.Name == spec.GetName()) { + if (location.ID == spec.GetID()) || (location.Name != "" && location.Name == spec.GetName()) { if p2, ok := n.Port(location.Port); ok { p1.Link(p2) diff --git a/pkg/node/onetomany.go b/pkg/node/onetomany.go index 57fd88ff..308c5dc1 100644 --- a/pkg/node/onetomany.go +++ b/pkg/node/onetomany.go @@ -4,7 +4,6 @@ import ( "sync" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/packet" "github.com/siyul-park/uniflow/pkg/port" "github.com/siyul-park/uniflow/pkg/process" @@ -35,7 +34,7 @@ func NewOneToManyNode(config OneToManyNodeConfig) *OneToManyNode { id := config.ID action := config.Action - if util.IsZero(id) { + if id == (ulid.ULID{}) { id = ulid.Make() } if action == nil { diff --git a/pkg/node/onetoone.go b/pkg/node/onetoone.go index 0c0a28a8..b7dc08d2 100644 --- a/pkg/node/onetoone.go +++ b/pkg/node/onetoone.go @@ -4,7 +4,6 @@ import ( "sync" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/packet" "github.com/siyul-park/uniflow/pkg/port" "github.com/siyul-park/uniflow/pkg/process" @@ -36,7 +35,7 @@ func NewOneToOneNode(config OneToOneNodeConfig) *OneToOneNode { id := config.ID action := config.Action - if util.IsZero(id) { + if id == (ulid.ULID{}) { id = ulid.Make() } if action == nil { diff --git a/pkg/plugin/controllx/switch.go b/pkg/plugin/controllx/switch.go index 841a6362..ed602009 100644 --- a/pkg/plugin/controllx/switch.go +++ b/pkg/plugin/controllx/switch.go @@ -1,10 +1,10 @@ package controllx import ( + "reflect" "sync" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/packet" "github.com/siyul-park/uniflow/pkg/port" @@ -91,7 +91,7 @@ func (n *SwitchNode) action(proc *process.Process, inPck *packet.Packet) ([]*pac } for _, cond := range n.conditions { - if output, _ := cond.when.Eval(input); !util.IsZero(output) { + if output, _ := cond.when.Eval(input); output != nil && !reflect.ValueOf(output).IsZero() { if i, ok := port.GetIndex(node.PortOut, cond.port); ok { outPcks := make([]*packet.Packet, i+1) outPcks[i] = inPck diff --git a/pkg/plugin/networkx/http.go b/pkg/plugin/networkx/http.go index f6873688..b8a91dfe 100644 --- a/pkg/plugin/networkx/http.go +++ b/pkg/plugin/networkx/http.go @@ -15,7 +15,6 @@ import ( "time" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/packet" "github.com/siyul-park/uniflow/pkg/port" @@ -258,7 +257,7 @@ func NewHTTPNode(config HTTPNodeConfig) *HTTPNode { id := config.ID address := config.Address - if util.IsZero(id) { + if id == (ulid.ULID{}) { id = ulid.Make() } diff --git a/pkg/plugin/networkx/mime.go b/pkg/plugin/networkx/mime.go index fa708e07..45060808 100644 --- a/pkg/plugin/networkx/mime.go +++ b/pkg/plugin/networkx/mime.go @@ -13,7 +13,7 @@ import ( "net/url" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" "github.com/siyul-park/uniflow/pkg/primitive" ) diff --git a/pkg/plugin/networkx/router.go b/pkg/plugin/networkx/router.go index 946b2781..49792e04 100644 --- a/pkg/plugin/networkx/router.go +++ b/pkg/plugin/networkx/router.go @@ -147,11 +147,11 @@ func (n *RouterNode) action(proc *process.Process, inPck *packet.Packet) ([]*pac if !ok { return nil, packet.NewError(node.ErrInvalidPacket, inPck) } - method, ok := primitive.Get[string](inPayload, KeyMethod) + method, ok := primitive.Pick[string](inPayload, KeyMethod) if !ok { return nil, packet.NewError(node.ErrInvalidPacket, inPck) } - path, ok := primitive.Get[string](inPayload, KeyPath) + path, ok := primitive.Pick[string](inPayload, KeyPath) if !ok { return nil, packet.NewError(node.ErrInvalidPacket, inPck) } diff --git a/pkg/plugin/systemx/reflect.go b/pkg/plugin/systemx/reflect.go index f7797808..be7924a1 100644 --- a/pkg/plugin/systemx/reflect.go +++ b/pkg/plugin/systemx/reflect.go @@ -4,7 +4,7 @@ import ( "context" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" + "github.com/samber/lo" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/node" "github.com/siyul-park/uniflow/pkg/packet" @@ -121,7 +121,7 @@ func (n *ReflectNode) action(proc *process.Process, inPck *packet.Packet) (*pack } specs, err = n.storage.FindMany(ctx, storage.Where[ulid.ULID](scheme.KeyID).IN(ids...), &database.FindOptions{ - Limit: util.Ptr[int](len(ids)), + Limit: lo.ToPtr(len(ids)), }) if err != nil { return nil, packet.NewError(err, inPck) @@ -162,14 +162,14 @@ func (n *ReflectNode) action(proc *process.Process, inPck *packet.Packet) (*pack for i, spec := range specs { id := spec.GetID() - if !util.IsZero(id) { + if id != (ulid.ULID{}) { ids = append(ids, id) patches[id] = examples[i] } } specs, err := n.storage.FindMany(ctx, storage.Where[ulid.ULID](scheme.KeyID).IN(ids...), &database.FindOptions{ - Limit: util.Ptr[int](len(ids)), + Limit: lo.ToPtr(len(ids)), }) if err != nil { return nil, packet.NewError(err, inPck) @@ -197,7 +197,7 @@ func (n *ReflectNode) action(proc *process.Process, inPck *packet.Packet) (*pack } specs, err = n.storage.FindMany(ctx, storage.Where[ulid.ULID](scheme.KeyID).IN(ids...), &database.FindOptions{ - Limit: util.Ptr[int](len(ids)), + Limit: lo.ToPtr(len(ids)), }) if err != nil { return nil, packet.NewError(err, inPck) @@ -227,16 +227,16 @@ func examplesToFilter(examples []*primitive.Map) (*storage.Filter, error) { return nil, err } - if !util.IsZero(spec.ID) { + if spec.ID != (ulid.ULID{}) { sub = sub.And(storage.Where[ulid.ULID](scheme.KeyID).EQ(spec.ID)) } - if !util.IsZero(spec.Kind) { + if spec.Kind != "" { sub = sub.And(storage.Where[string](scheme.KeyKind).EQ(spec.Kind)) } - if !util.IsZero(spec.Name) { + if spec.Name != "" { sub = sub.And(storage.Where[string](scheme.KeyName).EQ(spec.Name)) } - if !util.IsZero(spec.Namespace) { + if spec.Namespace != "" { sub = sub.And(storage.Where[string](scheme.KeyName).EQ(spec.Namespace)) } diff --git a/pkg/primitive/binary.go b/pkg/primitive/binary.go index b7a8aa0d..59c49fbd 100644 --- a/pkg/primitive/binary.go +++ b/pkg/primitive/binary.go @@ -3,11 +3,10 @@ package primitive import ( "encoding" "fmt" - "hash/fnv" "reflect" "github.com/pkg/errors" - encoding2 "github.com/siyul-park/uniflow/internal/encoding" + encoding2 "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -42,12 +41,30 @@ func (o Binary) Kind() Kind { return KindBinary } -func (o Binary) Hash() uint32 { - h := fnv.New32() - h.Write([]byte{byte(KindBinary), 0}) - h.Write([]byte(o)) +func (o Binary) Compare(v Object) int { + if r, ok := v.(Binary); !ok { + if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + for i := 0; i < o.Len(); i++ { + if r.Len() == i { + return 1 + } - return h.Sum32() + v1 := o.Get(i) + v2 := r.Get(i) + + if v1 > v2 { + return 1 + } else if v1 < v2 { + return -1 + } + } + return 0 + } } func (o Binary) Interface() any { diff --git a/pkg/primitive/binary_test.go b/pkg/primitive/binary_test.go index 3f48fd17..15e0f362 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,15 @@ func TestBinary_Get(t *testing.T) { assert.Equal(t, byte(0), v.Get(0)) } +func TestBinary_Compare(t *testing.T) { + v1 := NewBinary([]byte{0}) + v2 := NewBinary([]byte{1}) + + assert.Equal(t, 0, v1.Compare(v1)) + assert.Equal(t, -1, v1.Compare(v2)) + assert.Equal(t, 1, v2.Compare(v1)) +} + func TestBinary_Encode(t *testing.T) { e := NewBinaryEncoder() diff --git a/pkg/primitive/bool.go b/pkg/primitive/bool.go index a098dad6..4bfa38ac 100644 --- a/pkg/primitive/bool.go +++ b/pkg/primitive/bool.go @@ -1,11 +1,10 @@ package primitive import ( - "hash/fnv" "reflect" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -33,18 +32,20 @@ func (o Bool) Bool() bool { func (o Bool) Kind() Kind { return KindBool } - -func (o Bool) Hash() uint32 { - var v byte - if o { - v |= 1 +func (o Bool) Compare(v Object) int { + if r, ok := v.(Bool); !ok { + if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else if o == r { + return 0 + } else if o == TRUE { + return 1 + } else { + return -1 } - - h := fnv.New32() - h.Write([]byte{byte(KindBool), 0}) - h.Write([]byte{v}) - - return h.Sum32() } func (o Bool) Interface() any { diff --git a/pkg/primitive/bool_test.go b/pkg/primitive/bool_test.go index 643b4161..4c241c59 100644 --- a/pkg/primitive/bool_test.go +++ b/pkg/primitive/bool_test.go @@ -13,10 +13,11 @@ func TestNewBool(t *testing.T) { assert.Equal(t, true, v.Interface()) } -func TestBool_Hash(t *testing.T) { - assert.NotEqual(t, TRUE.Hash(), FALSE.Hash()) - assert.Equal(t, TRUE.Hash(), TRUE.Hash()) - assert.Equal(t, FALSE.Hash(), FALSE.Hash()) +func TestBool_Compare(t *testing.T) { + assert.Equal(t, 0, TRUE.Compare(TRUE)) + assert.Equal(t, 0, FALSE.Compare(FALSE)) + assert.Equal(t, 1, TRUE.Compare(FALSE)) + assert.Equal(t, -1, FALSE.Compare(TRUE)) } func TestBool_Encode(t *testing.T) { diff --git a/pkg/primitive/compare.go b/pkg/primitive/compare.go new file mode 100644 index 00000000..580525cf --- /dev/null +++ b/pkg/primitive/compare.go @@ -0,0 +1,20 @@ +package primitive + +type ( + ordered interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string + } +) + +func compare[T ordered](x, y T) int { + if x == y { + return 0 + } + if x > y { + return 1 + } + if x < y { + return -1 + } + return 0 +} diff --git a/pkg/primitive/encoding.go b/pkg/primitive/encoding.go index d612007a..71f2f54b 100644 --- a/pkg/primitive/encoding.go +++ b/pkg/primitive/encoding.go @@ -3,7 +3,7 @@ package primitive import ( "reflect" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) var ( diff --git a/pkg/primitive/float.go b/pkg/primitive/float.go index 93f7585a..5c3c3ebd 100644 --- a/pkg/primitive/float.go +++ b/pkg/primitive/float.go @@ -1,13 +1,10 @@ package primitive import ( - "encoding/binary" - "hash/fnv" - "math" "reflect" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -38,15 +35,34 @@ func (o Float32) Kind() Kind { return KindFloat32 } -func (o Float32) Hash() uint32 { - var buf [4]byte - binary.BigEndian.PutUint32(buf[:], math.Float32bits(float32(o))) - - h := fnv.New32() - h.Write([]byte{byte(KindFloat32), 0}) - h.Write(buf[:]) +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() + } +} - return h.Sum32() +func (o Float32) Compare(v Object) int { + if r, ok := v.(Float); !ok { + if r, ok := v.(Integer); ok { + return compare[float64](o.Float(), float64(r.Int())) + } else if r, ok := v.(Uinteger); ok { + return compare[float64](o.Float(), float64(r.Uint())) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[float64](o.Float(), r.Float()) + } } func (o Float32) Interface() any { @@ -67,15 +83,20 @@ func (o Float64) Kind() Kind { return KindFloat64 } -func (o Float64) Hash() uint32 { - var buf [8]byte - binary.BigEndian.PutUint64(buf[:], math.Float64bits(float64(o))) - - h := fnv.New32() - h.Write([]byte{byte(KindFloat64), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Float64) Compare(v Object) int { + if r, ok := v.(Float); !ok { + if r, ok := v.(Integer); ok { + return compare[float64](o.Float(), float64(r.Int())) + } else if r, ok := v.(Uinteger); ok { + return compare[float64](o.Float(), float64(r.Uint())) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[float64](o.Float(), r.Float()) + } } func (o Float64) Interface() any { diff --git a/pkg/primitive/float_test.go b/pkg/primitive/float_test.go index 6de5373d..26513e23 100644 --- a/pkg/primitive/float_test.go +++ b/pkg/primitive/float_test.go @@ -21,17 +21,19 @@ func TestNewFloat(t *testing.T) { }) } -func TestFloat_Hash(t *testing.T) { +func TestFloat_Compare(t *testing.T) { t.Run("32", func(t *testing.T) { - assert.NotEqual(t, NewFloat32(0).Hash(), NewFloat32(1).Hash()) - assert.Equal(t, NewFloat32(0).Hash(), NewFloat32(0).Hash()) - assert.Equal(t, NewFloat32(1).Hash(), NewFloat32(1).Hash()) + assert.Equal(t, 0, NewFloat32(0).Compare(NewFloat32(0))) + assert.Equal(t, 0, NewFloat32(0).Compare(NewFloat64(0))) + assert.Equal(t, 1, NewFloat32(1).Compare(NewFloat32(0))) + assert.Equal(t, -1, NewFloat32(0).Compare(NewFloat32(1))) }) t.Run("64", func(t *testing.T) { - assert.NotEqual(t, NewFloat64(0).Hash(), NewFloat64(1).Hash()) - assert.Equal(t, NewFloat64(0).Hash(), NewFloat64(0).Hash()) - assert.Equal(t, NewFloat64(1).Hash(), NewFloat64(1).Hash()) + assert.Equal(t, 0, NewFloat64(0).Compare(NewFloat64(0))) + assert.Equal(t, 0, NewFloat64(0).Compare(NewFloat32(0))) + assert.Equal(t, 1, NewFloat64(1).Compare(NewFloat64(0))) + assert.Equal(t, -1, NewFloat64(0).Compare(NewFloat64(1))) }) } diff --git a/pkg/primitive/int.go b/pkg/primitive/int.go index 804271a9..bf917c25 100644 --- a/pkg/primitive/int.go +++ b/pkg/primitive/int.go @@ -1,12 +1,10 @@ package primitive import ( - "hash/fnv" "reflect" - "unsafe" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -46,14 +44,20 @@ func (o Int) Kind() Kind { return KindInt } -func (o Int) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindInt), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Int) Compare(v Object) int { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return compare[int64](o.Int(), int64(r.Uint())) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Int()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[int64](o.Int(), r.Int()) + } } func (o Int) Interface() any { @@ -74,14 +78,20 @@ func (o Int8) Kind() Kind { return KindInt8 } -func (o Int8) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindInt8), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Int8) Compare(v Object) int { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return compare[int64](o.Int(), int64(r.Uint())) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Int()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[int64](o.Int(), r.Int()) + } } func (o Int8) Interface() any { @@ -102,14 +112,20 @@ func (o Int16) Kind() Kind { return KindInt16 } -func (o Int16) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindInt16), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Int16) Compare(v Object) int { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return compare[int64](o.Int(), int64(r.Uint())) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Int()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[int64](o.Int(), r.Int()) + } } func (o Int16) Interface() any { @@ -130,14 +146,20 @@ func (o Int32) Kind() Kind { return KindInt32 } -func (o Int32) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindInt32), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Int32) Compare(v Object) int { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return compare[int64](o.Int(), int64(r.Uint())) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Int()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[int64](o.Int(), r.Int()) + } } func (o Int32) Interface() any { @@ -158,14 +180,20 @@ func (o Int64) Kind() Kind { return KindInt64 } -func (o Int64) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindInt64), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Int64) Compare(v Object) int { + if r, ok := v.(Integer); !ok { + if r, ok := v.(Uinteger); ok { + return compare[int64](o.Int(), int64(r.Uint())) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Int()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[int64](o.Int(), r.Int()) + } } func (o Int64) Interface() any { diff --git a/pkg/primitive/int_test.go b/pkg/primitive/int_test.go index 7bfc7b87..30c1e866 100644 --- a/pkg/primitive/int_test.go +++ b/pkg/primitive/int_test.go @@ -39,31 +39,36 @@ func TestNewInt(t *testing.T) { }) } -func TestInt_Hash(t *testing.T) { +func TestInt_Compare(t *testing.T) { t.Run("", func(t *testing.T) { - assert.NotEqual(t, NewInt(0).Hash(), NewInt(1).Hash()) - assert.Equal(t, NewInt(0).Hash(), NewInt(0).Hash()) - assert.Equal(t, NewInt(1).Hash(), NewInt(1).Hash()) + assert.Equal(t, 0, NewInt(0).Compare(NewInt(0))) + assert.Equal(t, 1, NewInt(1).Compare(NewInt(0))) + assert.Equal(t, -1, NewInt(0).Compare(NewInt(1))) + assert.Equal(t, 0, NewInt(0).Compare(NewFloat32(0))) }) t.Run("8", func(t *testing.T) { - assert.NotEqual(t, NewInt8(0).Hash(), NewInt8(1).Hash()) - assert.Equal(t, NewInt8(0).Hash(), NewInt8(0).Hash()) - assert.Equal(t, NewInt8(1).Hash(), NewInt8(1).Hash()) + assert.Equal(t, 0, NewInt8(0).Compare(NewInt(0))) + assert.Equal(t, 1, NewInt8(1).Compare(NewInt(0))) + assert.Equal(t, -1, NewInt8(0).Compare(NewInt(1))) + assert.Equal(t, 0, NewInt8(0).Compare(NewFloat32(0))) }) t.Run("16", func(t *testing.T) { - assert.NotEqual(t, NewInt16(0).Hash(), NewInt16(1).Hash()) - assert.Equal(t, NewInt16(0).Hash(), NewInt16(0).Hash()) - assert.Equal(t, NewInt16(1).Hash(), NewInt16(1).Hash()) + assert.Equal(t, 0, NewInt16(0).Compare(NewInt(0))) + assert.Equal(t, 1, NewInt16(1).Compare(NewInt(0))) + assert.Equal(t, -1, NewInt16(0).Compare(NewInt(1))) + assert.Equal(t, 0, NewInt16(0).Compare(NewFloat32(0))) }) t.Run("32", func(t *testing.T) { - assert.NotEqual(t, NewInt32(0).Hash(), NewInt32(1).Hash()) - assert.Equal(t, NewInt32(0).Hash(), NewInt32(0).Hash()) - assert.Equal(t, NewInt32(1).Hash(), NewInt32(1).Hash()) + assert.Equal(t, 0, NewInt32(0).Compare(NewInt(0))) + assert.Equal(t, 1, NewInt32(1).Compare(NewInt(0))) + assert.Equal(t, -1, NewInt32(0).Compare(NewInt(1))) + assert.Equal(t, 0, NewInt32(0).Compare(NewFloat32(0))) }) t.Run("64", func(t *testing.T) { - assert.NotEqual(t, NewInt64(0).Hash(), NewInt64(1).Hash()) - assert.Equal(t, NewInt64(0).Hash(), NewInt64(0).Hash()) - assert.Equal(t, NewInt64(1).Hash(), NewInt64(1).Hash()) + assert.Equal(t, 0, NewInt64(0).Compare(NewInt(0))) + assert.Equal(t, 1, NewInt64(1).Compare(NewInt(0))) + assert.Equal(t, -1, NewInt64(0).Compare(NewInt(1))) + assert.Equal(t, 0, NewInt64(0).Compare(NewFloat32(0))) }) } diff --git a/pkg/primitive/map.go b/pkg/primitive/map.go index 8009f84a..fdde0247 100644 --- a/pkg/primitive/map.go +++ b/pkg/primitive/map.go @@ -2,15 +2,13 @@ package primitive import ( "fmt" - "hash/fnv" "reflect" "strings" - "unsafe" "github.com/benbjohnson/immutable" "github.com/iancoleman/strcase" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -107,31 +105,42 @@ func (o *Map) Kind() Kind { return KindMap } -func (o *Map) Hash() uint32 { - h := fnv.New32() - h.Write([]byte{byte(KindMap), 0}) +func (o *Map) Compare(v Object) int { + if r, ok := v.(*Map); !ok { + if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + keys1 := o.Keys() + keys2 := r.Keys() - itr := o.value.Iterator() - for !itr.Done() { - k, v, _ := itr.Next() + for i, k1 := range keys1 { + if len(keys2) == i { + return 1 + } - if k != nil { - hash := k.Hash() - buf := *(*[unsafe.Sizeof(hash)]byte)(unsafe.Pointer(&hash)) - h.Write(buf[:]) - } else { - h.Write([]byte{0}) + k2 := keys2[i] + if diff := Compare(k1, k2); diff != 0 { + return diff + } + + v1, ok1 := o.Get(k1) + v2, ok2 := o.Get(k2) + if diff := Compare(NewBool(ok1), NewBool(ok2)); diff != 0 { + return diff + } + if diff := Compare(v1, v2); diff != 0 { + return diff + } } - if v != nil { - hash := v.Hash() - buf := *(*[unsafe.Sizeof(hash)]byte)(unsafe.Pointer(&hash)) - h.Write(buf[:]) - } else { - h.Write([]byte{0}) + + if len(keys2) > len(keys1) { + return -1 } + return 0 } - - return h.Sum32() } func (o *Map) Interface() any { @@ -182,27 +191,7 @@ func (o *Map) Interface() any { } func (*comparer) Compare(a Object, b Object) int { - if a == nil { - return -1 - } else if b == nil { - return 1 - } else if a.Kind() > b.Kind() { - return 1 - } else if a.Kind() < b.Kind() { - return -1 - } - - hashA := a.Hash() - hashB := b.Hash() - - if hashA > hashB { - return 1 - } else if hashA < hashB { - return -1 - } - - // FIXME: hash conflict. - return 0 + return Compare(a, b) } // NewMapEncoder is encode map or struct to Map. diff --git a/pkg/primitive/map_test.go b/pkg/primitive/map_test.go index fd599552..c2a867bf 100644 --- a/pkg/primitive/map_test.go +++ b/pkg/primitive/map_test.go @@ -17,18 +17,6 @@ func TestNewMap(t *testing.T) { assert.Equal(t, map[string]string{k1.String(): v1.String()}, o.Interface()) } -func TestMap_Hash(t *testing.T) { - k1 := NewString(faker.Word()) - k2 := NewString(faker.Word()) - v1 := NewString(faker.Word()) - v2 := NewString(faker.Word()) - - assert.NotEqual(t, NewMap(k1, v1).Hash(), NewMap(k2, v2).Hash()) - assert.Equal(t, NewMap().Hash(), NewMap().Hash()) - assert.Equal(t, NewMap(k1, v1).Hash(), NewMap(k1, v1).Hash()) - assert.Equal(t, NewMap(k1, v1, k2, v2).Hash(), NewMap(k2, v2, k1, v1).Hash()) -} - func TestMap_GetAndSetAndDelete(t *testing.T) { k1 := NewString(faker.Word()) v1 := NewString(faker.Word()) @@ -89,7 +77,7 @@ func TestMap_Encode(t *testing.T) { K1: v1.String(), }) assert.NoError(t, err) - assert.Equal(t, NewMap(NewString("k_1"), v1).Hash(), v.Hash()) + assert.True(t, NewMap(NewString("k_1"), v1).Compare(v) == 0) }) } diff --git a/pkg/primitive/object.go b/pkg/primitive/object.go index 3939177c..f216b51f 100644 --- a/pkg/primitive/object.go +++ b/pkg/primitive/object.go @@ -1,12 +1,10 @@ package primitive -import "github.com/siyul-park/uniflow/internal/util" - type ( // Object is an atomic type. Object interface { Kind() Kind - Hash() uint32 + Compare(v Object) int Interface() any } @@ -34,10 +32,20 @@ const ( KindString ) -func Interface(v any) any { - if util.IsNil(v) { - return nil - } else if v, ok := v.(Object); !ok { +func Compare(x, y Object) int { + if x == nil && y == nil { + return 0 + } else if x == nil { + return -1 + } else if y == nil { + return 1 + } else { + return x.Compare(y) + } +} + +func Interface(v Object) any { + if v == nil { return nil } else { return v.Interface() diff --git a/pkg/primitive/getter.go b/pkg/primitive/pick.go similarity index 94% rename from pkg/primitive/getter.go rename to pkg/primitive/pick.go index b0ed5ff7..dea96aa8 100644 --- a/pkg/primitive/getter.go +++ b/pkg/primitive/pick.go @@ -10,7 +10,7 @@ var ( numberSubPath = regexp.MustCompile(`\[([0-9]+)\]`) ) -func Get[T any](v Object, path string) (T, bool) { +func Pick[T any](v Object, path string) (T, bool) { paths := parsePath(path) var zero T diff --git a/pkg/primitive/pointer.go b/pkg/primitive/pointer.go index df4cfd34..b7d10716 100644 --- a/pkg/primitive/pointer.go +++ b/pkg/primitive/pointer.go @@ -4,14 +4,13 @@ import ( "reflect" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" - "github.com/siyul-park/uniflow/internal/util" + "github.com/siyul-park/uniflow/pkg/encoding" ) // NewPointerEncoder is encode *T to T. func NewPointerEncoder(encoder encoding.Encoder[any, Object]) encoding.Encoder[any, Object] { return encoding.EncoderFunc[any, Object](func(source any) (Object, error) { - if util.IsNil(source) { + if source == nil { return nil, nil } if s := reflect.ValueOf(source); s.Kind() == reflect.Pointer { @@ -24,7 +23,7 @@ func NewPointerEncoder(encoder encoding.Encoder[any, Object]) encoding.Encoder[a // NewPointerDecoder is decode T to *T. func NewPointerDecoder(decoder encoding.Decoder[Object, any]) encoding.Decoder[Object, any] { return encoding.DecoderFunc[Object, any](func(source Object, target any) error { - if util.IsNil(source) { + if source == nil { return nil } if t := reflect.ValueOf(target); t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Pointer { diff --git a/pkg/primitive/shortcut.go b/pkg/primitive/shortcut.go index 2af7e91b..3f4b172f 100644 --- a/pkg/primitive/shortcut.go +++ b/pkg/primitive/shortcut.go @@ -2,7 +2,7 @@ package primitive import ( "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) // NewPointerEncoder is encode Object to Object. diff --git a/pkg/primitive/slice.go b/pkg/primitive/slice.go index 7faa9ba6..0fbfb386 100644 --- a/pkg/primitive/slice.go +++ b/pkg/primitive/slice.go @@ -2,13 +2,11 @@ package primitive import ( "fmt" - "hash/fnv" "reflect" - "unsafe" "github.com/benbjohnson/immutable" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -80,24 +78,29 @@ func (o *Slice) Kind() Kind { return KindSlice } -func (o *Slice) Hash() uint32 { - h := fnv.New32() - h.Write([]byte{byte(KindSlice), 0}) +func (o *Slice) Compare(v Object) int { + if r, ok := v.(*Slice); !ok { + if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + for i := 0; i < o.Len(); i++ { + if r.Len() == i { + return 1 + } - itr := o.value.Iterator() - for !itr.Done() { - _, v := itr.Next() + if diff := Compare(o.Get(i), r.Get(i)); diff != 0 { + return diff + } + } - if v != nil { - hash := v.Hash() - buf := *(*[unsafe.Sizeof(hash)]byte)(unsafe.Pointer(&hash)) - h.Write(buf[:]) - } else { - h.Write([]byte{0}) + if o.Len() > r.Len() { + return -1 } + return 0 } - - return h.Sum32() } func (o *Slice) Interface() any { diff --git a/pkg/primitive/slice_test.go b/pkg/primitive/slice_test.go index a2f2bd0b..8a48381a 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,15 @@ func TestSlice_Sub(t *testing.T) { assert.Equal(t, 1, o.Len()) } +func TestSlice_Compare(t *testing.T) { + v1 := NewString("1") + v2 := NewString("2") + + assert.Equal(t, 0, NewSlice(v1, v2).Compare(NewSlice(v1, v2))) + assert.Equal(t, 1, NewSlice(v2, v1).Compare(NewSlice(v1, v2))) + assert.Equal(t, -1, NewSlice(v1, v2).Compare(NewSlice(v2, v1))) +} + func TestSlice_Encode(t *testing.T) { e := NewSliceEncoder(NewStringEncoder()) diff --git a/pkg/primitive/string.go b/pkg/primitive/string.go index eb700178..4492954c 100644 --- a/pkg/primitive/string.go +++ b/pkg/primitive/string.go @@ -2,11 +2,10 @@ package primitive import ( "encoding" - "hash/fnv" "reflect" "github.com/pkg/errors" - encoding2 "github.com/siyul-park/uniflow/internal/encoding" + encoding2 "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -41,12 +40,16 @@ func (o String) Kind() Kind { return KindString } -func (o String) Hash() uint32 { - h := fnv.New32() - h.Write([]byte{byte(KindString), 0}) - h.Write([]byte(o)) - - return h.Sum32() +func (o String) Compare(v Object) int { + if r, ok := v.(String); !ok { + if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[string](o.String(), r.String()) + } } func (o String) Interface() any { diff --git a/pkg/primitive/string_test.go b/pkg/primitive/string_test.go index 10998c28..8f99a39a 100644 --- a/pkg/primitive/string_test.go +++ b/pkg/primitive/string_test.go @@ -14,13 +14,6 @@ func TestNewString(t *testing.T) { assert.Equal(t, KindString, v.Kind()) assert.Equal(t, raw, v.Interface()) } - -func TestString_Hash(t *testing.T) { - assert.NotEqual(t, NewString("A").Hash(), NewString("B").Hash()) - assert.Equal(t, NewString("").Hash(), NewString("").Hash()) - assert.Equal(t, NewString("A").Hash(), NewString("A").Hash()) -} - func TestString_Get(t *testing.T) { v := NewString("A") @@ -28,6 +21,12 @@ func TestString_Get(t *testing.T) { assert.Equal(t, rune('A'), v.Get(0)) } +func TestString_Compare(t *testing.T) { + assert.Equal(t, 0, NewString("A").Compare(NewString("A"))) + assert.Equal(t, 1, NewString("a").Compare(NewString("A"))) + assert.Equal(t, -1, NewString("A").Compare(NewString("a"))) +} + func TestString_Encode(t *testing.T) { e := NewStringEncoder() diff --git a/pkg/primitive/uint.go b/pkg/primitive/uint.go index 8a8b4517..fc98dbe2 100644 --- a/pkg/primitive/uint.go +++ b/pkg/primitive/uint.go @@ -1,12 +1,10 @@ package primitive import ( - "hash/fnv" "reflect" - "unsafe" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" ) type ( @@ -46,14 +44,20 @@ func (o Uint) Kind() Kind { return KindUint } -func (o Uint) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindUint), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Uint) Compare(v Object) int { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return compare[int64](int64(o.Uint()), r.Int()) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Uint()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[uint64](o.Uint(), r.Uint()) + } } func (o Uint) Interface() any { @@ -74,14 +78,20 @@ func (o Uint8) Kind() Kind { return KindUint8 } -func (o Uint8) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindUint8), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Uint8) Compare(v Object) int { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return compare[int64](int64(o.Uint()), r.Int()) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Uint()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[uint64](o.Uint(), r.Uint()) + } } func (o Uint8) Interface() any { @@ -102,14 +112,20 @@ func (o Uint16) Kind() Kind { return KindUint16 } -func (o Uint16) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindUint16), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Uint16) Compare(v Object) int { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return compare[int64](int64(o.Uint()), r.Int()) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Uint()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[uint64](o.Uint(), r.Uint()) + } } func (o Uint16) Interface() any { @@ -130,14 +146,20 @@ func (o Uint32) Kind() Kind { return KindUint32 } -func (o Uint32) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindUint32), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Uint32) Compare(v Object) int { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return compare[int64](int64(o.Uint()), r.Int()) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Uint()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[uint64](o.Uint(), r.Uint()) + } } func (o Uint32) Interface() any { @@ -158,14 +180,20 @@ func (o Uint64) Kind() Kind { return KindUint64 } -func (o Uint64) Hash() uint32 { - buf := *(*[unsafe.Sizeof(o)]byte)(unsafe.Pointer(&o)) - - h := fnv.New32() - h.Write([]byte{byte(KindUint64), 0}) - h.Write(buf[:]) - - return h.Sum32() +func (o Uint64) Compare(v Object) int { + if r, ok := v.(Uinteger); !ok { + if r, ok := v.(Integer); ok { + return compare[int64](int64(o.Uint()), r.Int()) + } else if r, ok := v.(Float); ok { + return compare[float64](float64(o.Uint()), r.Float()) + } else if o.Kind() > v.Kind() { + return 1 + } else { + return -1 + } + } else { + return compare[uint64](o.Uint(), r.Uint()) + } } func (o Uint64) Interface() any { diff --git a/pkg/primitive/uint_test.go b/pkg/primitive/uint_test.go index 163b4372..c871acdf 100644 --- a/pkg/primitive/uint_test.go +++ b/pkg/primitive/uint_test.go @@ -39,31 +39,36 @@ func TestNewUint(t *testing.T) { }) } -func TestUint_Hash(t *testing.T) { +func TestUint_Compare(t *testing.T) { t.Run("", func(t *testing.T) { - assert.NotEqual(t, NewUint(0).Hash(), NewUint(1).Hash()) - assert.Equal(t, NewUint(0).Hash(), NewUint(0).Hash()) - assert.Equal(t, NewUint(1).Hash(), NewUint(1).Hash()) + assert.Equal(t, 0, NewUint(0).Compare(NewUint(0))) + assert.Equal(t, 1, NewUint(1).Compare(NewUint(0))) + assert.Equal(t, -1, NewUint(0).Compare(NewUint(1))) + assert.Equal(t, 0, NewUint(0).Compare(NewFloat32(0))) }) t.Run("8", func(t *testing.T) { - assert.NotEqual(t, NewUint8(0).Hash(), NewUint8(1).Hash()) - assert.Equal(t, NewUint8(0).Hash(), NewUint8(0).Hash()) - assert.Equal(t, NewUint8(1).Hash(), NewUint8(1).Hash()) + assert.Equal(t, 0, NewUint8(0).Compare(NewUint(0))) + assert.Equal(t, 1, NewUint8(1).Compare(NewUint(0))) + assert.Equal(t, -1, NewUint8(0).Compare(NewUint(1))) + assert.Equal(t, 0, NewUint8(0).Compare(NewFloat32(0))) }) t.Run("16", func(t *testing.T) { - assert.NotEqual(t, NewUint16(0).Hash(), NewUint16(1).Hash()) - assert.Equal(t, NewUint16(0).Hash(), NewUint16(0).Hash()) - assert.Equal(t, NewUint16(1).Hash(), NewUint16(1).Hash()) + assert.Equal(t, 0, NewUint16(0).Compare(NewUint(0))) + assert.Equal(t, 1, NewUint16(1).Compare(NewUint(0))) + assert.Equal(t, -1, NewUint16(0).Compare(NewUint(1))) + assert.Equal(t, 0, NewUint16(0).Compare(NewFloat32(0))) }) t.Run("32", func(t *testing.T) { - assert.NotEqual(t, NewUint32(0).Hash(), NewUint32(1).Hash()) - assert.Equal(t, NewUint32(0).Hash(), NewUint32(0).Hash()) - assert.Equal(t, NewUint32(1).Hash(), NewUint32(1).Hash()) + assert.Equal(t, 0, NewUint32(0).Compare(NewUint(0))) + assert.Equal(t, 1, NewUint32(1).Compare(NewUint(0))) + assert.Equal(t, -1, NewUint32(0).Compare(NewUint(1))) + assert.Equal(t, 0, NewUint32(0).Compare(NewFloat32(0))) }) t.Run("64", func(t *testing.T) { - assert.NotEqual(t, NewUint64(0).Hash(), NewUint64(1).Hash()) - assert.Equal(t, NewUint64(0).Hash(), NewUint64(0).Hash()) - assert.Equal(t, NewUint64(1).Hash(), NewUint64(1).Hash()) + assert.Equal(t, 0, NewUint64(0).Compare(NewUint(0))) + assert.Equal(t, 1, NewUint64(1).Compare(NewUint(0))) + assert.Equal(t, -1, NewUint64(0).Compare(NewUint(1))) + assert.Equal(t, 0, NewUint64(0).Compare(NewFloat32(0))) }) } diff --git a/pkg/scheme/codec.go b/pkg/scheme/codec.go index c3cce757..9f44ad9e 100644 --- a/pkg/scheme/codec.go +++ b/pkg/scheme/codec.go @@ -2,7 +2,7 @@ package scheme import ( "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" "github.com/siyul-park/uniflow/pkg/node" ) diff --git a/pkg/scheme/scheme.go b/pkg/scheme/scheme.go index 70caa97c..9d0e045d 100644 --- a/pkg/scheme/scheme.go +++ b/pkg/scheme/scheme.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/pkg/errors" - "github.com/siyul-park/uniflow/internal/encoding" + "github.com/siyul-park/uniflow/pkg/encoding" "github.com/siyul-park/uniflow/pkg/node" ) diff --git a/pkg/scheme/unstructured.go b/pkg/scheme/unstructured.go index ce438aba..4d85743d 100644 --- a/pkg/scheme/unstructured.go +++ b/pkg/scheme/unstructured.go @@ -4,7 +4,6 @@ import ( "sync" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/primitive" ) @@ -35,10 +34,10 @@ func NewUnstructured(doc *primitive.Map) *Unstructured { u := &Unstructured{doc: doc} - if v := u.GetID(); !util.IsZero(v) { + if v := u.GetID(); v != (ulid.ULID{}) { u.SetID(v) } - if v := u.GetLinks(); !util.IsZero(v) { + if v := u.GetLinks(); len(v) > 0 { u.SetLinks(v) } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index d1ee8cd3..8f1af900 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -2,10 +2,10 @@ package storage import ( "context" + "reflect" "sync" "github.com/oklog/ulid/v2" - "github.com/siyul-park/uniflow/internal/util" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/primitive" "github.com/siyul-park/uniflow/pkg/scheme" @@ -70,7 +70,7 @@ func New(ctx context.Context, config Config) (*Storage, error) { var ok bool for _, i := range exists { if i.Name == index.Name { - if ok := util.Equal(i, index); !ok { + if reflect.DeepEqual(i, index) { s.collection.Indexes().Drop(ctx, i.Name) } break @@ -113,7 +113,7 @@ func (s *Storage) InsertOne(ctx context.Context, spec scheme.Spec) (ulid.ULID, e if unstructured.GetNamespace() == "" { unstructured.SetNamespace(scheme.NamespaceDefault) } - if util.IsZero(unstructured.GetID()) { + if unstructured.GetID() == (ulid.ULID{}) { unstructured.SetID(ulid.Make()) } @@ -147,7 +147,7 @@ func (s *Storage) InsertMany(ctx context.Context, objs []scheme.Spec) ([]ulid.UL if unstructured.GetNamespace() == "" { unstructured.SetNamespace(scheme.NamespaceDefault) } - if util.IsZero(unstructured.GetID()) { + if unstructured.GetID() == (ulid.ULID{}) { unstructured.SetID(ulid.Make()) } @@ -182,7 +182,7 @@ func (s *Storage) UpdateOne(ctx context.Context, spec scheme.Spec) (bool, error) if unstructured.GetNamespace() == "" { unstructured.SetNamespace(scheme.NamespaceDefault) } - if util.IsZero(unstructured.GetID()) { + if unstructured.GetID() == (ulid.ULID{}) { return false, nil } @@ -209,7 +209,7 @@ func (s *Storage) UpdateMany(ctx context.Context, objs []scheme.Spec) (int, erro if unstructured.GetNamespace() == "" { unstructured.SetNamespace(scheme.NamespaceDefault) } - if util.IsZero(unstructured.GetID()) { + if unstructured.GetID() == (ulid.ULID{}) { continue }