diff --git a/autobatch/autobatch.go b/autobatch/autobatch.go index 4a19d90..0b5cbac 100644 --- a/autobatch/autobatch.go +++ b/autobatch/autobatch.go @@ -117,7 +117,7 @@ func (d *Datastore) Flush(ctx context.Context) error { } } // clear out buffer - d.buffer = make(map[ds.Key]op, d.maxBufferEntries) + clear(d.buffer) return b.Commit(ctx) } diff --git a/basic_ds.go b/basic_ds.go index 22cfd70..d8b57fb 100644 --- a/basic_ds.go +++ b/basic_ds.go @@ -27,7 +27,7 @@ func NewMapDatastore() (d *MapDatastore) { } // Put implements Datastore.Put -func (d *MapDatastore) Put(ctx context.Context, key Key, value []byte) (err error) { +func (d *MapDatastore) Put(ctx context.Context, key Key, value []byte) error { d.values[key] = value return nil } @@ -38,7 +38,7 @@ func (d *MapDatastore) Sync(ctx context.Context, prefix Key) error { } // Get implements Datastore.Get -func (d *MapDatastore) Get(ctx context.Context, key Key) (value []byte, err error) { +func (d *MapDatastore) Get(ctx context.Context, key Key) ([]byte, error) { val, found := d.values[key] if !found { return nil, ErrNotFound @@ -47,13 +47,13 @@ func (d *MapDatastore) Get(ctx context.Context, key Key) (value []byte, err erro } // Has implements Datastore.Has -func (d *MapDatastore) Has(ctx context.Context, key Key) (exists bool, err error) { +func (d *MapDatastore) Has(ctx context.Context, key Key) (bool, error) { _, found := d.values[key] return found, nil } // GetSize implements Datastore.GetSize -func (d *MapDatastore) GetSize(ctx context.Context, key Key) (size int, err error) { +func (d *MapDatastore) GetSize(ctx context.Context, key Key) (int, error) { if v, found := d.values[key]; found { return len(v), nil } @@ -61,7 +61,7 @@ func (d *MapDatastore) GetSize(ctx context.Context, key Key) (size int, err erro } // Delete implements Datastore.Delete -func (d *MapDatastore) Delete(ctx context.Context, key Key) (err error) { +func (d *MapDatastore) Delete(ctx context.Context, key Key) error { delete(d.values, key) return nil } @@ -124,7 +124,7 @@ func (d *LogDatastore) Children() []Datastore { } // Put implements Datastore.Put -func (d *LogDatastore) Put(ctx context.Context, key Key, value []byte) (err error) { +func (d *LogDatastore) Put(ctx context.Context, key Key, value []byte) error { log.Printf("%s: Put %s\n", d.Name, key) // log.Printf("%s: Put %s ```%s```", d.Name, key, value) return d.child.Put(ctx, key, value) @@ -137,25 +137,25 @@ func (d *LogDatastore) Sync(ctx context.Context, prefix Key) error { } // Get implements Datastore.Get -func (d *LogDatastore) Get(ctx context.Context, key Key) (value []byte, err error) { +func (d *LogDatastore) Get(ctx context.Context, key Key) ([]byte, error) { log.Printf("%s: Get %s\n", d.Name, key) return d.child.Get(ctx, key) } // Has implements Datastore.Has -func (d *LogDatastore) Has(ctx context.Context, key Key) (exists bool, err error) { +func (d *LogDatastore) Has(ctx context.Context, key Key) (bool, error) { log.Printf("%s: Has %s\n", d.Name, key) return d.child.Has(ctx, key) } // GetSize implements Datastore.GetSize -func (d *LogDatastore) GetSize(ctx context.Context, key Key) (size int, err error) { +func (d *LogDatastore) GetSize(ctx context.Context, key Key) (int, error) { log.Printf("%s: GetSize %s\n", d.Name, key) return d.child.GetSize(ctx, key) } // Delete implements Datastore.Delete -func (d *LogDatastore) Delete(ctx context.Context, key Key) (err error) { +func (d *LogDatastore) Delete(ctx context.Context, key Key) error { log.Printf("%s: Delete %s\n", d.Name, key) return d.child.Delete(ctx, key) } @@ -203,20 +203,20 @@ func (d *LogDatastore) Batch(ctx context.Context) (Batch, error) { } // Put implements Batch.Put -func (d *LogBatch) Put(ctx context.Context, key Key, value []byte) (err error) { +func (d *LogBatch) Put(ctx context.Context, key Key, value []byte) error { log.Printf("%s: BatchPut %s\n", d.Name, key) // log.Printf("%s: Put %s ```%s```", d.Name, key, value) return d.child.Put(ctx, key, value) } // Delete implements Batch.Delete -func (d *LogBatch) Delete(ctx context.Context, key Key) (err error) { +func (d *LogBatch) Delete(ctx context.Context, key Key) error { log.Printf("%s: BatchDelete %s\n", d.Name, key) return d.child.Delete(ctx, key) } // Commit implements Batch.Commit -func (d *LogBatch) Commit(ctx context.Context) (err error) { +func (d *LogBatch) Commit(ctx context.Context) error { log.Printf("%s: BatchCommit\n", d.Name) return d.child.Commit(ctx) } diff --git a/datastore.go b/datastore.go index f14f17a..36ed4bd 100644 --- a/datastore.go +++ b/datastore.go @@ -206,14 +206,13 @@ var ErrNotFound error = &dsError{error: errors.New("datastore: key not found"), // } func GetBackedHas(ctx context.Context, ds Read, key Key) (bool, error) { _, err := ds.Get(ctx, key) - switch err { - case nil: - return true, nil - case ErrNotFound: - return false, nil - default: + if err != nil { + if err == ErrNotFound { + return false, nil + } return false, err } + return true, nil } // GetBackedSize provides a default Datastore.GetSize implementation. @@ -224,10 +223,10 @@ func GetBackedHas(ctx context.Context, ds Read, key Key) (bool, error) { // } func GetBackedSize(ctx context.Context, ds Read, key Key) (int, error) { value, err := ds.Get(ctx, key) - if err == nil { - return len(value), nil + if err != nil { + return -1, err } - return -1, err + return len(value), nil } type Batch interface { diff --git a/key.go b/key.go index 345d943..2d0c041 100644 --- a/key.go +++ b/key.go @@ -3,6 +3,7 @@ package datastore import ( "encoding/json" "path" + "slices" "strings" dsq "github.com/ipfs/go-datastore/query" @@ -90,26 +91,37 @@ func (k Key) Equal(k2 Key) bool { return k.string == k2.string } -// Less checks whether this key is sorted lower than another. -func (k Key) Less(k2 Key) bool { +// Compare returns -1 if this key is sorted lower than another, 1 if this key +// is sorted greater that another, and 0 if the two keys sort the same. +func (k Key) Compare(k2 Key) int { list1 := k.List() list2 := k2.List() + list2end := len(list2) - 1 for i, c1 := range list1 { - if len(list2) < (i + 1) { - return false + if list2end < i { + return 1 } - c2 := list2[i] if c1 < c2 { - return true - } else if c1 > c2 { - return false + return -1 + } + if c1 > c2 { + return 1 } // c1 == c2, continue } - // list1 is shorter or exactly the same. - return len(list1) < len(list2) + // if list1 is shorter. + if len(list1) < len(list2) { + return -1 + } + // Lists are the same. + return 0 +} + +// Less checks whether this key is sorted lower than another. +func (k Key) Less(k2 Key) bool { + return k.Compare(k2) == -1 } // List returns the `list` representation of this Key. @@ -126,11 +138,8 @@ func (k Key) List() []string { // NewKey("/Actor:JohnCleese/MontyPython/Comedy") func (k Key) Reverse() Key { l := k.List() - r := make([]string, len(l)) - for i, e := range l { - r[len(l)-i-1] = e - } - return KeyWithNamespaces(r) + slices.Reverse(l) + return KeyWithNamespaces(l) } // Namespaces returns the `namespaces` making up this Key. diff --git a/keytransform/keytransform.go b/keytransform/keytransform.go index c1c7414..06e19a0 100644 --- a/keytransform/keytransform.go +++ b/keytransform/keytransform.go @@ -130,13 +130,12 @@ orders: continue case dsq.OrderByKey, *dsq.OrderByKey, dsq.OrderByKeyDescending, *dsq.OrderByKeyDescending: - // if the key transform preserves order, we can delegate - // to the child datastore. + // if the key transform preserves order, we can delegate to the + // child datastore. if orderPreserving { - // When sorting, we compare with the first - // Order, then, if equal, we compare with the - // second Order, etc. However, keys are _unique_ - // so we'll never apply any additional orders + // When sorting, we compare with the first Order, then, if + // equal, we compare with the second Order, etc. However, keys + // are _unique_ so we'll never apply any additional orders // after ordering by key. child.Orders = child.Orders[:i+1] break orders diff --git a/keytransform/keytransform_test.go b/keytransform/keytransform_test.go index 530eefe..f42668a 100644 --- a/keytransform/keytransform_test.go +++ b/keytransform/keytransform_test.go @@ -2,7 +2,7 @@ package keytransform_test import ( "context" - "sort" + "slices" "testing" ds "github.com/ipfs/go-datastore" @@ -71,8 +71,12 @@ func TestBasic(t *testing.T) { require.Equal(t, len(listA), len(listB)) // sort them cause yeah. - sort.Sort(ds.KeySlice(listA)) - sort.Sort(ds.KeySlice(listB)) + slices.SortFunc(listA, func(a, b ds.Key) int { + return a.Compare(b) + }) + slices.SortFunc(listB, func(a, b ds.Key) int { + return a.Compare(b) + }) for i, kA := range listA { kB := listB[i] diff --git a/mount/mount.go b/mount/mount.go index 96f6437..35eddfd 100644 --- a/mount/mount.go +++ b/mount/mount.go @@ -7,7 +7,7 @@ import ( "context" "errors" "fmt" - "sort" + "slices" "strings" "sync" @@ -33,9 +33,10 @@ type Mount struct { // to least specific. func New(mounts []Mount) *Datastore { // make a copy so we're sure it doesn't mutate - m := make([]Mount, len(mounts)) - copy(m, mounts) - sort.Slice(m, func(i, j int) bool { return m[i].Prefix.String() > m[j].Prefix.String() }) + m := slices.Clone(mounts) + slices.SortFunc(m, func(a, b Mount) int { + return strings.Compare(b.Prefix.String(), a.Prefix.String()) + }) return &Datastore{mounts: m} } diff --git a/namespace/namespace_test.go b/namespace/namespace_test.go index 792c0c2..2f6762c 100644 --- a/namespace/namespace_test.go +++ b/namespace/namespace_test.go @@ -2,7 +2,8 @@ package namespace_test import ( "context" - "sort" + "slices" + "strings" "testing" ds "github.com/ipfs/go-datastore" @@ -62,8 +63,12 @@ func testBasic(t *testing.T, prefix string) { require.Equal(t, len(listA), len(listB)) // sort them cause yeah. - sort.Sort(ds.KeySlice(listA)) - sort.Sort(ds.KeySlice(listB)) + slices.SortFunc(listA, func(a, b ds.Key) int { + return a.Compare(b) + }) + slices.SortFunc(listB, func(a, b ds.Key) int { + return a.Compare(b) + }) for i, kA := range listA { kB := listB[i] @@ -103,7 +108,9 @@ func TestQuery(t *testing.T) { results, err := qres.Rest() require.NoError(t, err) - sort.Slice(results, func(i, j int) bool { return results[i].Key < results[j].Key }) + slices.SortFunc(results, func(a, b dsq.Entry) int { + return strings.Compare(a.Key, b.Key) + }) for i, ent := range results { require.Equal(t, expect[i].Key, ent.Key) @@ -121,7 +128,9 @@ func TestQuery(t *testing.T) { results, err = qres.Rest() require.NoError(t, err) - sort.Slice(results, func(i, j int) bool { return results[i].Key < results[j].Key }) + slices.SortFunc(results, func(a, b dsq.Entry) int { + return strings.Compare(a.Key, b.Key) + }) for i, ent := range results { require.Equal(t, expect[i].Key, ent.Key) diff --git a/null_ds.go b/null_ds.go index 0fd1501..26ed0bb 100644 --- a/null_ds.go +++ b/null_ds.go @@ -25,7 +25,7 @@ func NewNullDatastore() *NullDatastore { } // Put implements Datastore.Put -func (d *NullDatastore) Put(ctx context.Context, key Key, value []byte) (err error) { +func (d *NullDatastore) Put(ctx context.Context, key Key, value []byte) error { return nil } @@ -35,22 +35,22 @@ func (d *NullDatastore) Sync(ctx context.Context, prefix Key) error { } // Get implements Datastore.Get -func (d *NullDatastore) Get(ctx context.Context, key Key) (value []byte, err error) { +func (d *NullDatastore) Get(ctx context.Context, key Key) ([]byte, error) { return nil, ErrNotFound } // Has implements Datastore.Has -func (d *NullDatastore) Has(ctx context.Context, key Key) (exists bool, err error) { +func (d *NullDatastore) Has(ctx context.Context, key Key) (bool, error) { return false, nil } // Has implements Datastore.GetSize -func (d *NullDatastore) GetSize(ctx context.Context, key Key) (size int, err error) { +func (d *NullDatastore) GetSize(ctx context.Context, key Key) (int, error) { return -1, ErrNotFound } // Delete implements Datastore.Delete -func (d *NullDatastore) Delete(ctx context.Context, key Key) (err error) { +func (d *NullDatastore) Delete(ctx context.Context, key Key) error { return nil } @@ -89,15 +89,15 @@ func (d *NullDatastore) NewTransaction(ctx context.Context, readOnly bool) (Txn, type nullTxn struct{} -func (t *nullTxn) Get(ctx context.Context, key Key) (value []byte, err error) { +func (t *nullTxn) Get(ctx context.Context, key Key) ([]byte, error) { return nil, nil } -func (t *nullTxn) Has(ctx context.Context, key Key) (exists bool, err error) { +func (t *nullTxn) Has(ctx context.Context, key Key) (bool, error) { return false, nil } -func (t *nullTxn) GetSize(ctx context.Context, key Key) (size int, err error) { +func (t *nullTxn) GetSize(ctx context.Context, key Key) (int, error) { return 0, nil } diff --git a/query/order.go b/query/order.go index 1993155..40926ed 100644 --- a/query/order.go +++ b/query/order.go @@ -2,7 +2,7 @@ package query import ( "bytes" - "sort" + "slices" "strings" ) @@ -70,8 +70,8 @@ func (OrderByKeyDescending) String() string { // Less returns true if a comes before b with the requested orderings. func Less(orders []Order, a, b Entry) bool { - for _, cmp := range orders { - switch cmp.Compare(a, b) { + for _, order := range orders { + switch order.Compare(a, b) { case 0: case -1: return true @@ -86,9 +86,23 @@ func Less(orders []Order, a, b Entry) bool { return a.Key < b.Key } +// Compare compares two Entry values according to the given orders. Returns -1 +// if Entry a comes before b with the requested ordering, 1 if a comes after b, +// and 0 if a and b are the same. +func Compare(orders []Order, a, b Entry) int { + for _, order := range orders { + n := order.Compare(a, b) + if n != 0 { + return n + } + } + // Gives stable sort. + return strings.Compare(a.Key, b.Key) +} + // Sort sorts the given entries using the given orders. func Sort(orders []Order, entries []Entry) { - sort.Slice(entries, func(i int, j int) bool { - return Less(orders, entries[i], entries[j]) + slices.SortFunc(entries, func(a, b Entry) int { + return Compare(orders, a, b) }) }