package ffi
import "C"
import (
"errors"
"fmt"
"unsafe"
)
type Iterator struct {
handle *C.IteratorHandle
batchSize int
loadedPairs []*ownedKeyValue
currentPair *ownedKeyValue
currentKey []byte
currentValue []byte
currentResource interface{ free() error }
err error
}
func (it *Iterator) freeCurrentAllocation() error {
if it.currentResource == nil {
return nil
}
e := it.currentResource.free()
it.currentResource = nil
return e
}
func (it *Iterator) nextInternal() error {
if len(it.loadedPairs) > 0 {
it.currentPair, it.loadedPairs = it.loadedPairs[0], it.loadedPairs[1:]
return nil
}
if e := it.freeCurrentAllocation(); e != nil {
return e
}
if it.batchSize <= 1 {
kv, e := getKeyValueFromResult(C.fwd_iter_next(it.handle))
if e != nil {
return e
}
it.currentPair = kv
it.currentResource = kv
} else {
batch, e := getKeyValueBatchFromResult(C.fwd_iter_next_n(it.handle, C.size_t(it.batchSize)))
if e != nil {
return e
}
pairs := batch.copy()
if len(pairs) > 0 {
it.currentPair, it.loadedPairs = pairs[0], pairs[1:]
} else {
it.currentPair = nil
}
it.currentResource = batch
}
return nil
}
func (it *Iterator) SetBatchSize(batchSize int) {
it.batchSize = batchSize
}
func (it *Iterator) Next() bool {
it.err = it.nextInternal()
if it.currentPair == nil || it.err != nil {
return false
}
k, v := it.currentPair.copy()
it.currentKey = k
it.currentValue = v
return true
}
func (it *Iterator) NextBorrowed() bool {
it.err = it.nextInternal()
if it.currentPair == nil || it.err != nil {
return false
}
it.currentKey = it.currentPair.key.BorrowedBytes()
it.currentValue = it.currentPair.value.BorrowedBytes()
it.err = nil
return true
}
func (it *Iterator) Key() []byte {
if it.currentPair == nil || it.err != nil {
return nil
}
return it.currentKey
}
func (it *Iterator) Value() []byte {
if it.currentPair == nil || it.err != nil {
return nil
}
return it.currentValue
}
func (it *Iterator) Err() error {
return it.err
}
func (it *Iterator) Drop() error {
err := it.freeCurrentAllocation()
if it.handle != nil {
err = errors.Join(
err,
getErrorFromVoidResult(C.fwd_free_iterator(it.handle)))
it.handle = nil
}
return err
}
func getIteratorFromIteratorResult(result C.IteratorResult) (*Iterator, error) {
switch result.tag {
case C.IteratorResult_NullHandlePointer:
return nil, errDBClosed
case C.IteratorResult_Ok:
body := (*C.IteratorResult_Ok_Body)(unsafe.Pointer(&result.anon0))
proposal := &Iterator{
handle: body.handle,
}
return proposal, nil
case C.IteratorResult_Err:
err := newOwnedBytes(*(*C.OwnedBytes)(unsafe.Pointer(&result.anon0))).intoError()
return nil, err
default:
return nil, fmt.Errorf("unknown C.IteratorResult tag: %d", result.tag)
}
}