95 lines
2.8 KiB
Nim
95 lines
2.8 KiB
Nim
|
# Simple Key-Value store database interface
|
||
|
|
||
|
import
|
||
|
tables, hashes, sets
|
||
|
|
||
|
type
|
||
|
MemoryStoreRef* = ref object of RootObj
|
||
|
records: Table[seq[byte], seq[byte]]
|
||
|
|
||
|
DataProc* = proc(val: openArray[byte])
|
||
|
PutProc = proc (db: RootRef, key, val: openArray[byte]) {.gcsafe.}
|
||
|
GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): bool {.gcsafe.}
|
||
|
DelProc = proc (db: RootRef, key: openArray[byte]) {.gcsafe.}
|
||
|
ContainsProc = proc (db: RootRef, key: openArray[byte]): bool {.gcsafe.}
|
||
|
|
||
|
KVStoreRef* = ref object
|
||
|
## Key-Value store virtual interface
|
||
|
obj: RootRef
|
||
|
putProc: PutProc
|
||
|
getProc: GetProc
|
||
|
delProc: DelProc
|
||
|
containsProc: ContainsProc
|
||
|
|
||
|
template put*(db: KVStoreRef, key, val: openArray[byte]) =
|
||
|
## Store ``value`` at ``key`` - overwrites existing value if already present
|
||
|
db.putProc(db.obj, key, val)
|
||
|
|
||
|
template get*(db: KVStoreRef, key: openArray[byte], onData: untyped): 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.
|
||
|
db.getProc(db.obj, key, onData)
|
||
|
|
||
|
template del*(db: KVStoreRef, key: openArray[byte]) =
|
||
|
## Remove value at ``key`` from store - do nothing if the value is not present
|
||
|
db.delProc(db.obj, key)
|
||
|
|
||
|
template contains*(db: KVStoreRef, key: openArray[byte]): bool =
|
||
|
## Return true iff ``key`` has a value in store
|
||
|
db.containsProc(db.obj, key)
|
||
|
|
||
|
proc get*(db: MemoryStoreRef, key: openArray[byte], onData: DataProc): bool =
|
||
|
let key = @key
|
||
|
db.records.withValue(key, v):
|
||
|
onData(v[])
|
||
|
return true
|
||
|
|
||
|
proc del*(db: MemoryStoreRef, key: openArray[byte]) =
|
||
|
# TODO: This is quite inefficient and it won't be necessary once
|
||
|
# https://github.com/nim-lang/Nim/issues/7457 is developed.
|
||
|
let key = @key
|
||
|
db.records.del(key)
|
||
|
|
||
|
proc contains*(db: MemoryStoreRef, key: openArray[byte]): bool =
|
||
|
db.records.contains(@key)
|
||
|
|
||
|
proc put*(db: MemoryStoreRef, key, val: openArray[byte]) =
|
||
|
# TODO: This is quite inefficient and it won't be necessary once
|
||
|
# https://github.com/nim-lang/Nim/issues/7457 is developed.
|
||
|
let key = @key
|
||
|
db.records[key] = @val
|
||
|
|
||
|
proc init*(T: type MemoryStoreRef): T =
|
||
|
T(
|
||
|
records: initTable[seq[byte], seq[byte]]()
|
||
|
)
|
||
|
|
||
|
proc putImpl[T](db: RootRef, key, val: openArray[byte]) =
|
||
|
mixin put
|
||
|
put(T(db), key, val)
|
||
|
|
||
|
proc getImpl[T](db: RootRef, key: openArray[byte], onData: DataProc): bool =
|
||
|
mixin get
|
||
|
get(T(db), key, onData)
|
||
|
|
||
|
proc delImpl[T](db: RootRef, key: openArray[byte]) =
|
||
|
mixin del
|
||
|
del(T(db), key)
|
||
|
|
||
|
proc containsImpl[T](db: RootRef, key: openArray[byte]): bool =
|
||
|
mixin contains
|
||
|
contains(T(db), key)
|
||
|
|
||
|
func kvStore*[T: RootRef](x: T): KVStoreRef =
|
||
|
mixin del, get, put, contains
|
||
|
|
||
|
KVStoreRef(
|
||
|
obj: x,
|
||
|
putProc: putImpl[T],
|
||
|
getProc: getImpl[T],
|
||
|
delProc: delImpl[T],
|
||
|
containsProc: containsImpl[T]
|
||
|
)
|