2020-04-27 13:16:11 +00:00
|
|
|
# beacon_chain
|
|
|
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
|
|
|
## Simple Key-Value store database interface that allows creating multiple
|
|
|
|
## tables within each store
|
|
|
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
|
|
|
import
|
2021-04-06 11:33:24 +00:00
|
|
|
std/[tables, hashes, sets],
|
|
|
|
stew/results
|
2020-04-27 13:16:11 +00:00
|
|
|
|
|
|
|
export results
|
|
|
|
|
|
|
|
type
|
|
|
|
MemStoreRef* = ref object of RootObj
|
|
|
|
records: Table[seq[byte], seq[byte]]
|
|
|
|
# TODO interaction with this table would benefit from heterogenous lookup
|
|
|
|
# (see `@key` below)
|
|
|
|
# https://github.com/nim-lang/Nim/issues/7457
|
|
|
|
|
|
|
|
KvResult*[T] = Result[T, string]
|
|
|
|
|
|
|
|
DataProc* = proc(val: openArray[byte]) {.gcsafe, raises: [Defect].}
|
2021-05-17 13:55:57 +00:00
|
|
|
KeyValueProc* = proc(key, val: openArray[byte]) {.gcsafe, raises: [Defect].}
|
2020-04-27 13:16:11 +00:00
|
|
|
|
|
|
|
PutProc = proc (db: RootRef, key, val: openArray[byte]): KvResult[void] {.nimcall, gcsafe, raises: [Defect].}
|
|
|
|
GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): KvResult[bool] {.nimcall, gcsafe, raises: [Defect].}
|
2021-05-17 13:55:57 +00:00
|
|
|
FindProc = proc (db: RootRef, prefix: openArray[byte], onFind: KeyValueProc): KvResult[int] {.nimcall, gcsafe, raises: [Defect].}
|
2020-04-27 13:16:11 +00:00
|
|
|
DelProc = proc (db: RootRef, key: openArray[byte]): KvResult[void] {.nimcall, gcsafe, raises: [Defect].}
|
|
|
|
ContainsProc = proc (db: RootRef, key: openArray[byte]): KvResult[bool] {.nimcall, gcsafe, raises: [Defect].}
|
2020-09-11 13:05:52 +00:00
|
|
|
CloseProc = proc (db: RootRef): KvResult[void] {.nimcall, gcsafe, raises: [Defect].}
|
2020-04-27 13:16:11 +00:00
|
|
|
|
|
|
|
KvStoreRef* = ref object
|
|
|
|
## Key-Value store virtual interface
|
|
|
|
obj: RootRef
|
|
|
|
putProc: PutProc
|
|
|
|
getProc: GetProc
|
2021-05-17 13:55:57 +00:00
|
|
|
findProc: FindProc
|
2020-04-27 13:16:11 +00:00
|
|
|
delProc: DelProc
|
|
|
|
containsProc: ContainsProc
|
2020-09-11 13:05:52 +00:00
|
|
|
closeProc: CloseProc
|
2020-04-27 13:16:11 +00:00
|
|
|
|
|
|
|
template put*(dbParam: KvStoreRef, key, val: openArray[byte]): KvResult[void] =
|
|
|
|
## Store ``value`` at ``key`` - overwrites existing value if already present
|
|
|
|
let db = dbParam
|
|
|
|
db.putProc(db.obj, key, val)
|
|
|
|
|
|
|
|
template get*(dbParam: KvStoreRef, key: openArray[byte], onData: untyped): KvResult[bool] =
|
|
|
|
## Retrive value at ``key`` and call ``onData`` with the value. The data is
|
|
|
|
## valid for the duration of the callback.
|
|
|
|
## ``onData``: ``proc(data: openArray[byte])``
|
|
|
|
## returns true if found and false otherwise.
|
|
|
|
let db = dbParam
|
|
|
|
db.getProc(db.obj, key, onData)
|
|
|
|
|
2021-05-17 13:55:57 +00:00
|
|
|
template find*(
|
|
|
|
dbParam: KvStoreRef, prefix: openArray[byte], onFind: untyped): KvResult[int] =
|
|
|
|
## Perform a prefix find, returning all data starting with the given prefix.
|
|
|
|
## An empty prefix returns all rows in the store.
|
|
|
|
## The data is valid for the duration of the callback.
|
|
|
|
## ``onFind``: ``proc(key, value: openArray[byte])``
|
|
|
|
## returns the number of rows found
|
|
|
|
let db = dbParam
|
|
|
|
db.findProc(db.obj, prefix, onFind)
|
|
|
|
|
2020-04-27 13:16:11 +00:00
|
|
|
template del*(dbParam: KvStoreRef, key: openArray[byte]): KvResult[void] =
|
|
|
|
## Remove value at ``key`` from store - do nothing if the value is not present
|
|
|
|
let db = dbParam
|
|
|
|
db.delProc(db.obj, key)
|
|
|
|
|
|
|
|
template contains*(dbParam: KvStoreRef, key: openArray[byte]): KvResult[bool] =
|
|
|
|
## Return true iff ``key`` has a value in store
|
|
|
|
let db = dbParam
|
|
|
|
db.containsProc(db.obj, key)
|
|
|
|
|
2020-09-11 13:05:52 +00:00
|
|
|
template close*(dbParam: KvStoreRef): KvResult[void] =
|
|
|
|
## Close database
|
|
|
|
let db = dbParam
|
|
|
|
db.closeProc(db.obj)
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
proc putImpl[T](db: RootRef, key, val: openArray[byte]): KvResult[void] {.gcsafe.} =
|
2020-04-27 13:16:11 +00:00
|
|
|
mixin put
|
|
|
|
put(T(db), key, val)
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
proc getImpl[T](db: RootRef, key: openArray[byte], onData: DataProc): KvResult[bool] {.gcsafe.} =
|
2020-04-27 13:16:11 +00:00
|
|
|
mixin get
|
|
|
|
get(T(db), key, onData)
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
proc findImpl[T](db: RootRef, key: openArray[byte], onFind: KeyValueProc): KvResult[int] {.gcsafe.} =
|
2021-05-17 13:55:57 +00:00
|
|
|
mixin get
|
|
|
|
find(T(db), key, onFind)
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
proc delImpl[T](db: RootRef, key: openArray[byte]): KvResult[void] {.gcsafe.} =
|
2020-04-27 13:16:11 +00:00
|
|
|
mixin del
|
|
|
|
del(T(db), key)
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
proc containsImpl[T](db: RootRef, key: openArray[byte]): KvResult[bool] {.gcsafe.} =
|
2020-04-27 13:16:11 +00:00
|
|
|
mixin contains
|
|
|
|
contains(T(db), key)
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
proc closeImpl[T](db: RootRef): KvResult[void] {.gcsafe.} =
|
2020-09-11 13:05:52 +00:00
|
|
|
mixin close
|
|
|
|
close(T(db))
|
|
|
|
|
2020-04-27 13:16:11 +00:00
|
|
|
func kvStore*[T: RootRef](x: T): KvStoreRef =
|
2020-09-11 13:05:52 +00:00
|
|
|
mixin del, get, put, contains, close
|
2020-04-27 13:16:11 +00:00
|
|
|
|
|
|
|
KvStoreRef(
|
|
|
|
obj: x,
|
|
|
|
putProc: putImpl[T],
|
|
|
|
getProc: getImpl[T],
|
2021-05-17 13:55:57 +00:00
|
|
|
findProc: findImpl[T],
|
2020-04-27 13:16:11 +00:00
|
|
|
delProc: delImpl[T],
|
2020-09-11 13:05:52 +00:00
|
|
|
containsProc: containsImpl[T],
|
|
|
|
closeProc: closeImpl[T]
|
2020-04-27 13:16:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc get*(db: MemStoreRef, key: openArray[byte], onData: DataProc): KvResult[bool] =
|
|
|
|
db.records.withValue(@key, v):
|
|
|
|
onData(v[])
|
|
|
|
return ok(true)
|
|
|
|
|
|
|
|
ok(false)
|
|
|
|
|
2021-05-17 13:55:57 +00:00
|
|
|
proc find*(
|
|
|
|
db: MemStoreRef, prefix: openArray[byte],
|
|
|
|
onFind: KeyValueProc): KvResult[int] =
|
|
|
|
var total = 0
|
|
|
|
# Should use lower/upper bounds instead
|
|
|
|
for k, v in db.records:
|
|
|
|
if k.len() >= prefix.len and k.toOpenArray(0, prefix.len() - 1) == prefix:
|
|
|
|
onFind(k, v)
|
|
|
|
total += 1
|
|
|
|
|
|
|
|
ok(total)
|
|
|
|
|
2020-04-27 13:16:11 +00:00
|
|
|
proc del*(db: MemStoreRef, key: openArray[byte]): KvResult[void] =
|
|
|
|
db.records.del(@key)
|
|
|
|
ok()
|
|
|
|
|
|
|
|
proc contains*(db: MemStoreRef, key: openArray[byte]): KvResult[bool] =
|
|
|
|
ok(db.records.contains(@key))
|
|
|
|
|
|
|
|
proc put*(db: MemStoreRef, key, val: openArray[byte]): KvResult[void] =
|
|
|
|
db.records[@key] = @val
|
|
|
|
ok()
|
|
|
|
|
2020-09-11 13:05:52 +00:00
|
|
|
proc close*(db: MemStoreRef): KvResult[void] =
|
|
|
|
db.records.clear()
|
|
|
|
ok()
|
|
|
|
|
2020-04-27 13:16:11 +00:00
|
|
|
proc init*(T: type MemStoreRef): T =
|
|
|
|
T(
|
|
|
|
records: initTable[seq[byte], seq[byte]]()
|
|
|
|
)
|