mirror of
https://github.com/luxfi/database.git
synced 2026-10-30 15:01:18 +00:00
- Add //go:build leveldb to all leveldb package files - Add //go:build pebbledb to all pebbledb package files - Refactor factory to use registration pattern - Refactor manager to use registration pattern - badgerdb is now the only default database - Build with -tags=leveldb or -tags=pebbledb to enable them
140 lines
2.5 KiB
Go
140 lines
2.5 KiB
Go
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
//go:build pebbledb
|
|
|
|
package pebbledb
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"sync"
|
|
|
|
"github.com/cockroachdb/pebble"
|
|
|
|
"github.com/luxfi/database"
|
|
)
|
|
|
|
var (
|
|
_ database.Iterator = (*iter)(nil)
|
|
|
|
errCouldNotGetValue = errors.New("could not get iterator value")
|
|
)
|
|
|
|
type iter struct {
|
|
// [lock] ensures that only one goroutine can access [iter] at a time.
|
|
// Note that [Database.Close] calls [iter.Release] so we need [lock] to ensure
|
|
// that the user and [Database.Close] don't execute [iter.Release] concurrently.
|
|
// Invariant: [Database.lock] is never grabbed while holding [lock].
|
|
lock sync.Mutex
|
|
|
|
db *Database
|
|
iter *pebble.Iterator
|
|
|
|
initialized bool
|
|
closed bool
|
|
err error
|
|
|
|
hasNext bool
|
|
nextKey []byte
|
|
nextVal []byte
|
|
}
|
|
|
|
// Must not be called with [db.lock] held.
|
|
func (it *iter) Next() bool {
|
|
it.lock.Lock()
|
|
defer it.lock.Unlock()
|
|
|
|
switch {
|
|
case it.err != nil:
|
|
it.hasNext = false
|
|
return false
|
|
case it.closed:
|
|
it.hasNext = false
|
|
it.err = database.ErrClosed
|
|
return false
|
|
case !it.initialized:
|
|
it.hasNext = it.iter.First()
|
|
it.initialized = true
|
|
default:
|
|
it.hasNext = it.iter.Next()
|
|
}
|
|
|
|
if !it.hasNext {
|
|
return false
|
|
}
|
|
|
|
key := it.iter.Key()
|
|
value, err := it.iter.ValueAndErr()
|
|
if err != nil {
|
|
it.hasNext = false
|
|
it.err = fmt.Errorf("%w: %w", errCouldNotGetValue, err)
|
|
return false
|
|
}
|
|
|
|
it.nextKey = key
|
|
it.nextVal = value
|
|
return true
|
|
}
|
|
|
|
func (it *iter) Error() error {
|
|
it.lock.Lock()
|
|
defer it.lock.Unlock()
|
|
|
|
if it.err != nil || it.closed {
|
|
return it.err
|
|
}
|
|
return updateError(it.iter.Error())
|
|
}
|
|
|
|
func (it *iter) Key() []byte {
|
|
it.lock.Lock()
|
|
defer it.lock.Unlock()
|
|
|
|
if !it.hasNext {
|
|
return nil
|
|
}
|
|
return slices.Clone(it.nextKey)
|
|
}
|
|
|
|
func (it *iter) Value() []byte {
|
|
it.lock.Lock()
|
|
defer it.lock.Unlock()
|
|
|
|
if !it.hasNext {
|
|
return nil
|
|
}
|
|
return slices.Clone(it.nextVal)
|
|
}
|
|
|
|
func (it *iter) Release() {
|
|
it.db.lock.Lock()
|
|
defer it.db.lock.Unlock()
|
|
|
|
it.lock.Lock()
|
|
defer it.lock.Unlock()
|
|
|
|
it.release()
|
|
}
|
|
|
|
// Assumes [it.lock] and [it.db.lock] are held.
|
|
func (it *iter) release() {
|
|
if it.closed {
|
|
return
|
|
}
|
|
|
|
// Cloning these values ensures that calling it.Key() or it.Value() after
|
|
// releasing the iterator will not segfault.
|
|
it.nextKey = slices.Clone(it.nextKey)
|
|
it.nextVal = slices.Clone(it.nextVal)
|
|
|
|
// Remove the iterator from the list of open iterators.
|
|
it.db.openIterators.Remove(it)
|
|
|
|
it.closed = true
|
|
if err := it.iter.Close(); err != nil {
|
|
it.err = updateError(err)
|
|
}
|
|
}
|