Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Iterator Key() and Value() no longer return a copy #168

Merged
merged 12 commits into from
Jul 15, 2024
Merged
8 changes: 6 additions & 2 deletions backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ func verifyIterator(t *testing.T, itr Iterator, expected []int64, msg string) {

var list []int64
for itr.Valid() {
key := itr.Key()
key := make([]byte, len(itr.Key()))
copy(key, itr.Key())
list = append(list, bytes2Int64(key))
itr.Next()
}
Expand Down Expand Up @@ -450,7 +451,10 @@ func assertKeyValues(t *testing.T, db DB, expect map[string][]byte) {
actual := make(map[string][]byte)
for ; iter.Valid(); iter.Next() {
require.NoError(t, iter.Error())
actual[string(iter.Key())] = iter.Value()

value := make([]byte, len(iter.Value()))
copy(value, iter.Value())
actual[string(iter.Key())] = value
}

assert.Equal(t, expect, actual)
Expand Down
9 changes: 6 additions & 3 deletions badger_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,19 +279,22 @@ func (i *badgerDBIterator) Valid() bool {
return true
}

// Key implements Iterator.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Key implements Iterator.

I'd remove these comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I would leave them, because I think they give readers some additional context about the functions.
Granted, in the context of this repo it's clear what the functions refer to.
Still, I would argue it doesn't hurt to leave extra-info in.

// The caller should not modify the contents of the returned slice.
// Instead, the caller should make a copy and work on the copy.
func (i *badgerDBIterator) Key() []byte {
if !i.Valid() {
panic("iterator is invalid")
}
// Note that we don't use KeyCopy, so this is only valid until the next
// call to Next.
return i.iter.Item().KeyCopy(nil)
return i.iter.Item().Key()
}

// Value implements Iterator.
alesforz marked this conversation as resolved.
Show resolved Hide resolved
func (i *badgerDBIterator) Value() []byte {
if !i.Valid() {
panic("iterator is invalid")
}

val, err := i.iter.Item().ValueCopy(nil)
if err != nil {
i.lastErr = err
Expand Down
4 changes: 3 additions & 1 deletion common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ func checkKeyPanics(t *testing.T, itr Iterator) {

func checkValuePanics(t *testing.T, itr Iterator) {
t.Helper()
assert.Panics(t, func() { itr.Value() })

msg := "checkValuePanics expected panic but didn't"
assert.Panics(t, func() { itr.Value() }, msg)
}

func newTempDB(t *testing.T, backend BackendType) (db DB, dbDir string) {
Expand Down
12 changes: 6 additions & 6 deletions goleveldb_iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,19 @@ func (itr *goLevelDBIterator) Valid() bool {
}

// Key implements Iterator.
// The caller should not modify the contents of the returned slice.
// Instead, the caller should make a copy and work on the copy.
func (itr *goLevelDBIterator) Key() []byte {
// Key returns a copy of the current key.
// See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88
itr.assertIsValid()
return cp(itr.source.Key())
return itr.source.Key()
}

// Value implements Iterator.
// The caller should not modify the contents of the returned slice.
// Instead, the caller should make a copy and work on the copy.
func (itr *goLevelDBIterator) Value() []byte {
// Value returns a copy of the current value.
// See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88
itr.assertIsValid()
return cp(itr.source.Value())
return itr.source.Value()
}

// Next implements Iterator.
Expand Down
12 changes: 6 additions & 6 deletions pebble.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,19 +396,19 @@ func (itr *pebbleDBIterator) Valid() bool {
}

// Key implements Iterator.
// The caller should not modify the contents of the returned slice.
// Instead, the caller should make a copy and work on the copy.
func (itr *pebbleDBIterator) Key() []byte {
// Key returns a copy of the current key.
// See https://github.com/cockroachdb/pebble/blob/v1.0.0/iterator.go#L2106
itr.assertIsValid()
return cp(itr.source.Key())
return itr.source.Key()
}

// Value implements Iterator.
// The caller should not modify the contents of the returned slice.
// Instead, the caller should make a copy and work on the copy.
func (itr *pebbleDBIterator) Value() []byte {
// Value returns a copy of the current value.
// See https://github.com/cockroachdb/pebble/blob/v1.0.0/iterator.go#L2116
itr.assertIsValid()
return cp(itr.source.Value())
return itr.source.Value()
}

// Next implements Iterator.
Expand Down
10 changes: 8 additions & 2 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,17 @@ type Iterator interface {
Next()

// Key returns the key at the current position. Panics if the iterator is invalid.
// CONTRACT: key readonly []byte
// Key returns the key of the current key/value pair, or nil if done.
// The caller should not modify the contents of the returned slice, and
// its contents may change on the next call to any 'seeks method'.
// Instead, the caller should make a copy and work on the copy.
Key() (key []byte)

// Value returns the value at the current position. Panics if the iterator is invalid.
// CONTRACT: value readonly []byte
// Value returns the value of the current key/value pair, or nil if done.
// The caller should not modify the contents of the returned slice, and
// its contents may change on the next call to any 'seeks method'.
// Instead, the caller should make a copy and work on the copy.
Value() (value []byte)

// Error returns the last error encountered by the iterator, if any.
Expand Down
Loading