Files
2026-08-25 21:33:06 +02:00

200 lines
4.0 KiB
Go

package db0501
import (
"bytes"
"slices"
)
type KV struct {
log Log
keys [][]byte
vals [][]byte
}
func (kv *KV) Open() error {
if err := kv.log.Open(); err != nil {
return err
}
entries := []Entry{}
for {
ent := Entry{}
eof, err := kv.log.Read(&ent)
if err != nil {
return err
} else if eof {
break
}
entries = append(entries, ent)
}
slices.SortStableFunc(entries, func(a, b Entry) int {
return bytes.Compare(a.key, b.key)
})
kv.keys, kv.vals = kv.keys[:0], kv.vals[:0]
for _, ent := range entries {
n := len(kv.keys)
if n > 0 && bytes.Equal(kv.keys[n-1], ent.key) {
kv.keys, kv.vals = kv.keys[:n-1], kv.vals[:n-1]
}
if !ent.deleted {
kv.keys = append(kv.keys, ent.key)
kv.vals = append(kv.vals, ent.val)
}
}
return nil
}
func (kv *KV) Close() error { return kv.log.Close() }
func (kv *KV) Get(key []byte) (val []byte, ok bool, err error) {
if idx, ok := slices.BinarySearchFunc(kv.keys, key, bytes.Compare); ok {
return kv.vals[idx], true, nil
}
return nil, false, nil
}
type UpdateMode int
const (
ModeUpsert UpdateMode = 0 // insert or update
ModeInsert UpdateMode = 1 // insert new
ModeUpdate UpdateMode = 2 // update existing
)
func (kv *KV) SetEx(key []byte, val []byte, mode UpdateMode) (updated bool, err error) {
idx, exist := slices.BinarySearchFunc(kv.keys, key, bytes.Compare)
switch mode {
case ModeUpsert:
updated = !exist || !bytes.Equal(kv.vals[idx], val)
case ModeInsert:
updated = !exist
case ModeUpdate:
updated = exist && !bytes.Equal(kv.vals[idx], val)
default:
panic("unreachable")
}
if updated {
if err = kv.log.Write(&Entry{key: key, val: val}); err != nil {
return false, err
}
if exist {
kv.vals[idx] = val
} else {
kv.keys = slices.Insert(kv.keys, idx, key)
kv.vals = slices.Insert(kv.vals, idx, val)
}
}
return
}
func (kv *KV) Set(key []byte, val []byte) (updated bool, err error) {
return kv.SetEx(key, val, ModeUpsert)
}
func (kv *KV) Del(key []byte) (deleted bool, err error) {
if idx, ok := slices.BinarySearchFunc(kv.keys, key, bytes.Compare); ok {
if err = kv.log.Write(&Entry{key: key, deleted: true}); err != nil {
return false, err
}
kv.keys = slices.Delete(kv.keys, idx, idx+1)
kv.vals = slices.Delete(kv.vals, idx, idx+1)
return true, nil
}
return false, nil
}
type KVIterator struct {
keys [][]byte
vals [][]byte
pos int
}
func (kv *KV) Seek(key []byte) (*KVIterator, error) {
pos, _ := slices.BinarySearchFunc(kv.keys, key, bytes.Compare)
return &KVIterator{keys: kv.keys, vals: kv.vals, pos: pos}, nil
}
func (iter *KVIterator) Valid() bool {
return 0 <= iter.pos && iter.pos < len(iter.keys)
}
func (iter *KVIterator) Key() []byte {
check(iter.Valid())
return iter.keys[iter.pos]
}
func (iter *KVIterator) Val() []byte {
check(iter.Valid())
return iter.vals[iter.pos]
}
func (iter *KVIterator) Next() error {
if iter.pos < len(iter.keys) {
iter.pos++
}
return nil
}
func (iter *KVIterator) Prev() error {
if iter.pos >= 0 {
iter.pos--
}
return nil
}
type RangedKVIter struct {
iter KVIterator
stop []byte
desc bool
}
func (iter *RangedKVIter) Valid() bool {
if !iter.iter.Valid() {
return false
}
r := bytes.Compare(iter.iter.Key(), iter.stop)
if iter.desc && r < 0 {
return false
} else if !iter.desc && r > 0 {
return false
}
return true
}
func (iter *RangedKVIter) Key() []byte {
check(iter.Valid())
return iter.iter.Key()
}
func (iter *RangedKVIter) Val() []byte {
check(iter.Valid())
return iter.iter.Val()
}
func (iter *RangedKVIter) Next() error {
if !iter.Valid() {
return nil
}
if iter.desc {
return iter.iter.Prev()
} else {
return iter.iter.Next()
}
}
func (kv *KV) Range(start, stop []byte, desc bool) (*RangedKVIter, error) {
iter, err := kv.Seek(start)
if err != nil {
return nil, err
}
if desc && (!iter.Valid() || bytes.Compare(iter.Key(), start) > 0) {
if err = iter.Prev(); err != nil {
return nil, err
}
}
return &RangedKVIter{iter: *iter, stop: stop, desc: desc}, nil
}
// QzBQWVJJOUhU https://trialofcode.org/