erigon-pulse/ethdb/kv_bolt.go
Alex Sharov b490192e67
Use KV Abstraction in RestAPI (#400)
* Introduce NoValuesCursor. From() method is useless because can be replaced by Seek().`
* implement NoValueCursor interface
* use abstract db in restapi
* cleanup .md
2020-03-24 09:12:55 +07:00

261 lines
4.4 KiB
Go

package ethdb
import (
"bytes"
"context"
"github.com/ledgerwatch/bolt"
)
type BoltKV struct {
opts Options
bolt *bolt.DB
}
// Close closes BoltKV
// All transactions must be closed before closing the database.
func (db *BoltKV) Close() error {
return db.bolt.Close()
}
func (db *BoltKV) Options() Options {
return db.opts
}
type boltTx struct {
ctx context.Context
db *BoltKV
bolt *bolt.Tx
}
type boltBucket struct {
tx *boltTx
bolt *bolt.Bucket
nameLen uint
}
type boltCursor struct {
ctx context.Context
bucket boltBucket
prefix []byte
bolt *bolt.Cursor
k []byte
v []byte
err error
}
func (db *BoltKV) View(ctx context.Context, f func(tx Tx) error) (err error) {
t := &boltTx{db: db, ctx: ctx}
return db.bolt.View(func(tx *bolt.Tx) error {
defer t.cleanup()
t.bolt = tx
return f(t)
})
}
func (db *BoltKV) Update(ctx context.Context, f func(tx Tx) error) (err error) {
t := &boltTx{db: db, ctx: ctx}
return db.bolt.Update(func(tx *bolt.Tx) error {
defer t.cleanup()
t.bolt = tx
return f(t)
})
}
func (tx *boltTx) Bucket(name []byte) Bucket {
b := boltBucket{tx: tx, nameLen: uint(len(name))}
b.bolt = tx.bolt.Bucket(name)
return b
}
func (tx *boltTx) cleanup() {
// nothing to cleanup
}
func (c *boltCursor) Prefix(v []byte) Cursor {
c.prefix = v
return c
}
func (c *boltCursor) MatchBits(n uint) Cursor {
panic("not implemented yet")
}
func (c *boltCursor) Prefetch(v uint) Cursor {
// nothing to do
return c
}
func (c *boltCursor) NoValues() NoValuesCursor {
return &noValuesBoltCursor{boltCursor: *c}
}
func (b boltBucket) Get(key []byte) (val []byte, err error) {
select {
case <-b.tx.ctx.Done():
return nil, b.tx.ctx.Err()
default:
}
val, _ = b.bolt.Get(key)
return val, err
}
func (b boltBucket) Put(key []byte, value []byte) error {
select {
case <-b.tx.ctx.Done():
return b.tx.ctx.Err()
default:
}
return b.bolt.Put(key, value)
}
func (b boltBucket) Delete(key []byte) error {
select {
case <-b.tx.ctx.Done():
return b.tx.ctx.Err()
default:
}
return b.bolt.Delete(key)
}
func (b boltBucket) Cursor() Cursor {
c := &boltCursor{bucket: b, ctx: b.tx.ctx}
// nothing to do
return c
}
func (c *boltCursor) initCursor() {
if c.bolt != nil {
return
}
c.bolt = c.bucket.bolt.Cursor()
}
func (c *boltCursor) First() ([]byte, []byte, error) {
c.initCursor()
if c.prefix != nil {
c.k, c.v = c.bolt.Seek(c.prefix)
} else {
c.k, c.v = c.bolt.First()
}
return c.k, c.v, c.err
}
func (c *boltCursor) Seek(seek []byte) ([]byte, []byte, error) {
select {
case <-c.ctx.Done():
return nil, nil, c.ctx.Err()
default:
}
c.initCursor()
c.k, c.v = c.bolt.Seek(seek)
return c.k, c.v, c.err
}
func (c *boltCursor) Next() ([]byte, []byte, error) {
select {
case <-c.ctx.Done():
return nil, nil, c.ctx.Err()
default:
}
c.k, c.v = c.bolt.Next()
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
return nil, nil, nil
}
return c.k, c.v, c.err
}
func (c *boltCursor) Walk(walker func(k, v []byte) (bool, error)) error {
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
if err != nil {
return err
}
ok, err := walker(k, v)
if err != nil {
return err
}
if !ok {
return nil
}
}
return nil
}
type noValuesBoltCursor struct {
boltCursor
}
func (c *noValuesBoltCursor) Walk(walker func(k []byte, vSize uint64) (bool, error)) error {
for k, vSize, err := c.First(); k != nil || err != nil; k, vSize, err = c.Next() {
if err != nil {
return err
}
ok, err := walker(k, vSize)
if err != nil {
return err
}
if !ok {
return nil
}
}
return nil
}
func (c *noValuesBoltCursor) First() ([]byte, uint64, error) {
c.initCursor()
var vSize uint64
var v []byte
if c.prefix != nil {
c.k, v = c.bolt.Seek(c.prefix)
} else {
c.k, v = c.bolt.First()
}
vSize = uint64(len(v))
return c.k, vSize, c.err
}
func (c *noValuesBoltCursor) Seek(seek []byte) ([]byte, uint64, error) {
select {
case <-c.ctx.Done():
return nil, 0, c.ctx.Err()
default:
}
c.initCursor()
var vSize uint64
var v []byte
c.k, v = c.bolt.Seek(seek)
vSize = uint64(len(v))
return c.k, vSize, c.err
}
func (c *noValuesBoltCursor) Next() ([]byte, uint64, error) {
select {
case <-c.ctx.Done():
return nil, 0, c.ctx.Err()
default:
}
var vSize uint64
var v []byte
c.k, v = c.bolt.Next()
vSize = uint64(len(v))
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
return nil, 0, nil
}
return c.k, vSize, c.err
}