keyed_queue: avoid copies, lookups (#220)
This change avoids many superfluous data copies and lookups by exploiting `withValue` thus performing only one lookup with the same key (instead of hasKey + actual lookup). We also avoid several copies of the value which often is copied even though only the next/prev pointers from the item are needed, for examle during shifting and lru-appending.
This commit is contained in:
parent
a0a53c9116
commit
28743363ff
|
@ -22,17 +22,14 @@
|
||||||
## semantics, this means that `=` performs a deep copy of the allocated queue
|
## semantics, this means that `=` performs a deep copy of the allocated queue
|
||||||
## which is refered to the deep copy semantics of the underlying table driver.
|
## which is refered to the deep copy semantics of the underlying table driver.
|
||||||
|
|
||||||
import
|
import std/tables, results
|
||||||
std/[math, tables],
|
|
||||||
./results
|
|
||||||
|
|
||||||
export
|
export results
|
||||||
results
|
|
||||||
|
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
type
|
type
|
||||||
KeyedQueueItem*[K,V] = object ##\
|
KeyedQueueItem*[K, V] = object
|
||||||
## Data value container as stored in the queue.
|
## Data value container as stored in the queue.
|
||||||
## There is a special requirements for `KeyedQueueItem` terminal nodes:
|
## There is a special requirements for `KeyedQueueItem` terminal nodes:
|
||||||
## *prv == nxt* so that there is no dangling link. On the flip side,
|
## *prv == nxt* so that there is no dangling link. On the flip side,
|
||||||
|
@ -41,33 +38,26 @@ type
|
||||||
data*: V ## Some data value, can freely be modified.
|
data*: V ## Some data value, can freely be modified.
|
||||||
kPrv*, kNxt*: K ## Queue links, read-only.
|
kPrv*, kNxt*: K ## Queue links, read-only.
|
||||||
|
|
||||||
KeyedQueuePair*[K,V] = object ##\
|
KeyedQueuePair*[K, V] = object ## Key-value pair, typically used as return code.
|
||||||
## Key-value pair, typically used as return code.
|
|
||||||
key: K ## Sorter key (read-only for consistency with `SLstResult[K,V]`)
|
key: K ## Sorter key (read-only for consistency with `SLstResult[K,V]`)
|
||||||
data*: V ## Some data value, to be modified freely
|
data*: V ## Some data value, to be modified freely
|
||||||
|
|
||||||
KeyedQueueTab*[K,V] = ##\
|
KeyedQueueTab*[K, V] = Table[K, KeyedQueueItem[K, V]]
|
||||||
## Internal table type exposed for debugging.
|
## Internal table type exposed for debugging.
|
||||||
Table[K,KeyedQueueItem[K,V]]
|
|
||||||
|
|
||||||
KeyedQueue*[K,V] = object ##\
|
KeyedQueue*[K, V] = object ## Data queue descriptor
|
||||||
## Data queue descriptor
|
|
||||||
tab*: KeyedQueueTab[K, V] ## Data table
|
tab*: KeyedQueueTab[K, V] ## Data table
|
||||||
kFirst*, kLast*: K ## Doubly linked item list queue
|
kFirst*, kLast*: K ## Doubly linked item list queue
|
||||||
|
|
||||||
BlindValue = ##\
|
BlindValue = distinct tuple[] ## Type name is syntactic sugar, used for key-only queues
|
||||||
## Type name is syntactic sugar, used for key-only queues
|
|
||||||
distinct byte
|
|
||||||
|
|
||||||
KeyedQueueNV*[K] = ##\
|
KeyedQueueNV*[K] = KeyedQueue[K, BlindValue] ## Key-only queue, no values
|
||||||
## Key-only queue, no values
|
|
||||||
KeyedQueue[K,BlindValue]
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Private helpers
|
# Private helpers
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
template noKeyError(info: static[string]; code: untyped) =
|
template noKeyError(info: static[string], code: untyped) =
|
||||||
try:
|
try:
|
||||||
code
|
code
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
|
@ -81,180 +71,193 @@ proc shiftImpl[K,V](rq: var KeyedQueue[K,V]) =
|
||||||
## Expects: rq.tab.len != 0
|
## Expects: rq.tab.len != 0
|
||||||
assert rq.tab.len != 0 # debugging only
|
assert rq.tab.len != 0 # debugging only
|
||||||
|
|
||||||
noKeyError("shiftImpl"):
|
let first = rq.kFirst
|
||||||
# Unqueue first item
|
rq.tab.withValue(first, item):
|
||||||
let item = rq.tab[rq.kFirst] # yes, crashes if `rq.tab.len == 0`
|
if rq.tab.len == 1: # item only
|
||||||
rq.tab.del(rq.kFirst)
|
|
||||||
|
|
||||||
if rq.tab.len == 0:
|
|
||||||
rq.kFirst.reset
|
rq.kFirst.reset
|
||||||
rq.kLast.reset
|
rq.kLast.reset
|
||||||
else:
|
else:
|
||||||
rq.kFirst = item.kNxt
|
rq.tab.withValue(item[].kNxt, next):
|
||||||
if rq.tab.len == 1:
|
if rq.tab.len == 2: # item and one more
|
||||||
rq.tab[rq.kFirst].kNxt = rq.kFirst # node points to itself
|
next[].kNxt = item[].kNxt # node points to itself
|
||||||
rq.tab[rq.kFirst].kPrv = rq.tab[rq.kFirst].kNxt # term nd has: nxt == prv
|
next[].kPrv = next[].kNxt # term nd has: nxt == prv
|
||||||
|
rq.kFirst = item[].kNxt
|
||||||
|
do:
|
||||||
|
raiseAssert "rq.kFirst missing"
|
||||||
|
|
||||||
|
rq.tab.del(first)
|
||||||
|
|
||||||
proc popImpl[K, V](rq: var KeyedQueue[K, V]) =
|
proc popImpl[K, V](rq: var KeyedQueue[K, V]) =
|
||||||
## Expects: rq.tab.len != 0
|
## Expects: rq.tab.len != 0
|
||||||
|
|
||||||
# Pop last item
|
# Pop last item
|
||||||
noKeyError("popImpl"):
|
let last = rq.kLast
|
||||||
let item = rq.tab[rq.kLast] # yes, crashes if `rq.tab.len == 0`
|
rq.tab.withValue(last, item):
|
||||||
rq.tab.del(rq.kLast)
|
if rq.tab.len == 1: # item only
|
||||||
|
|
||||||
if rq.tab.len == 0:
|
|
||||||
rq.kFirst.reset
|
rq.kFirst.reset
|
||||||
rq.kLast.reset
|
rq.kLast.reset
|
||||||
else:
|
else:
|
||||||
rq.kLast = item.kPrv
|
rq.tab.withValue(item[].kPrv, prev):
|
||||||
if rq.tab.len == 1:
|
if rq.tab.len == 2: # item and one more
|
||||||
rq.tab[rq.kLast].kPrv = rq.kLast # single node points to itself
|
prev[].kPrv = item[].kPrv # single node points to itself
|
||||||
rq.tab[rq.kLast].kNxt = rq.tab[rq.kLast].kPrv # term node has: nxt == prv
|
prev[].kNxt = prev[].kPrv # term node has: nxt == prv
|
||||||
|
rq.kLast = item[].kPrv
|
||||||
|
do:
|
||||||
|
raiseAssert "rq.kLast missing"
|
||||||
|
|
||||||
|
rq.tab.del(last)
|
||||||
|
|
||||||
proc deleteImpl[K,V](rq: var KeyedQueue[K,V]; key: K) =
|
proc deleteImpl[K, V](rq: var KeyedQueue[K, V], key: K) =
|
||||||
## Expects: rq.tab.hesKey(key)
|
## Expects: rq.tab.hesKey(key)
|
||||||
|
|
||||||
if rq.kFirst == key:
|
if rq.kFirst == key:
|
||||||
rq.shiftImpl
|
rq.shiftImpl
|
||||||
|
|
||||||
elif rq.kLast == key:
|
elif rq.kLast == key:
|
||||||
rq.popImpl
|
rq.popImpl
|
||||||
|
|
||||||
else:
|
else:
|
||||||
noKeyError("deleteImpl"):
|
rq.tab.withValue(key, item):
|
||||||
let item = rq.tab[key] # yes, crashes if `not rq.tab.hasKey(key)`
|
# now: 2 < rq.tab.len (otherwise rq.kFirst == key or rq.kLast == key)
|
||||||
|
rq.tab.withValue(rq.kFirst, first):
|
||||||
|
if first[].kNxt == key:
|
||||||
|
# item was the second one
|
||||||
|
first[].kPrv = item[].kNxt
|
||||||
|
rq.tab.withValue(rq.kLast, last):
|
||||||
|
if last.kPrv == key:
|
||||||
|
# item was one before last
|
||||||
|
last.kNxt = item[].kPrv
|
||||||
|
|
||||||
|
rq.tab.withValue(item[].kPrv, other):
|
||||||
|
other[].kNxt = item[].kNxt
|
||||||
|
rq.tab.withValue(item[].kNxt, other):
|
||||||
|
other[].kPrv = item[].kPrv
|
||||||
|
do:
|
||||||
|
raiseAssert "item missing"
|
||||||
|
|
||||||
rq.tab.del(key)
|
rq.tab.del(key)
|
||||||
|
|
||||||
# now: 2 < rq.tab.len (otherwise rq.kFirst == key or rq.kLast == key)
|
proc appendImpl[K, V](rq: var KeyedQueue[K, V], key: K, val: V) =
|
||||||
if rq.tab[rq.kFirst].kNxt == key:
|
|
||||||
# item was the second one
|
|
||||||
rq.tab[rq.kFirst].kPrv = item.kNxt
|
|
||||||
if rq.tab[rq.kLast].kPrv == key:
|
|
||||||
# item was one before last
|
|
||||||
rq.tab[rq.kLast].kNxt = item.kPrv
|
|
||||||
|
|
||||||
rq.tab[item.kPrv].kNxt = item.kNxt
|
|
||||||
rq.tab[item.kNxt].kPrv = item.kPrv
|
|
||||||
|
|
||||||
|
|
||||||
proc appendImpl[K,V](rq: var KeyedQueue[K,V]; key: K; val: V) =
|
|
||||||
## Expects: not rq.tab.hasKey(key)
|
## Expects: not rq.tab.hasKey(key)
|
||||||
|
|
||||||
# Append queue item
|
# Append queue item
|
||||||
var item = KeyedQueueItem[K, V](data: val)
|
var item = KeyedQueueItem[K, V](data: val)
|
||||||
|
|
||||||
noKeyError("appendImpl"):
|
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
rq.kFirst = key
|
rq.kFirst = key
|
||||||
item.kPrv = key
|
item.kPrv = key
|
||||||
else:
|
else:
|
||||||
if rq.kFirst == rq.kLast:
|
if rq.kFirst == rq.kLast:
|
||||||
rq.tab[rq.kFirst].kPrv = key # first terminal node
|
rq.tab.withValue(rq.kFirst, first):
|
||||||
rq.tab[rq.kLast].kNxt = key
|
first[].kPrv = key
|
||||||
|
first[].kNxt = key
|
||||||
|
else:
|
||||||
|
rq.tab.withValue(rq.kLast, last):
|
||||||
|
last[].kNxt = key
|
||||||
|
|
||||||
item.kPrv = rq.kLast
|
item.kPrv = rq.kLast
|
||||||
|
|
||||||
rq.kLast = key
|
rq.kLast = key
|
||||||
item.kNxt = item.kPrv # terminal node
|
item.kNxt = item.kPrv # terminal node
|
||||||
|
|
||||||
rq.tab[key] = item # yes, makes `verify()` fail if `rq.tab.hasKey(key)`
|
rq.tab[key] = move(item)
|
||||||
|
|
||||||
|
proc prependImpl[K, V](rq: var KeyedQueue[K, V], key: K, val: V) =
|
||||||
proc prependImpl[K,V](rq: var KeyedQueue[K,V]; key: K; val: V) =
|
|
||||||
## Expects: not rq.tab.hasKey(key)
|
## Expects: not rq.tab.hasKey(key)
|
||||||
|
|
||||||
# Prepend queue item
|
# Prepend queue item
|
||||||
var item = KeyedQueueItem[K, V](data: val)
|
var item = KeyedQueueItem[K, V](data: val)
|
||||||
|
|
||||||
noKeyError("prependImpl"):
|
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
rq.kLast = key
|
rq.kLast = key
|
||||||
item.kNxt = key
|
item.kNxt = key
|
||||||
else:
|
else:
|
||||||
if rq.kFirst == rq.kLast:
|
if rq.kFirst == rq.kLast:
|
||||||
rq.tab[rq.kLast].kNxt = key # first terminal node
|
rq.tab.withValue(rq.kLast, last):
|
||||||
rq.tab[rq.kFirst].kPrv = key
|
last[].kNxt = key
|
||||||
|
last[].kPrv = key
|
||||||
|
else:
|
||||||
|
rq.tab.withValue(rq.kFirst, first):
|
||||||
|
first[].kPrv = key
|
||||||
|
|
||||||
item.kNxt = rq.kFirst
|
item.kNxt = rq.kFirst
|
||||||
|
|
||||||
rq.kFirst = key
|
rq.kFirst = key
|
||||||
item.kPrv = item.kNxt # terminal node has: nxt == prv
|
item.kPrv = item.kNxt # terminal node has: nxt == prv
|
||||||
|
|
||||||
rq.tab[key] = item # yes, makes `verify()` fail if `rq.tab.hasKey(key)`
|
rq.tab[key] = move(item)
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
|
||||||
proc shiftKeyImpl[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc shiftKeyImpl[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
noKeyError("shiftKeyImpl"):
|
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
let key = rq.kFirst
|
let key = rq.kFirst
|
||||||
rq.shiftImpl
|
rq.shiftImpl
|
||||||
return ok(key)
|
Opt.some(key)
|
||||||
err()
|
else:
|
||||||
|
Opt.none(K)
|
||||||
|
|
||||||
proc popKeyImpl[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc popKeyImpl[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
noKeyError("popKeyImpl"):
|
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
let key = rq.kLast
|
let key = rq.kLast
|
||||||
rq.popImpl
|
rq.popImpl
|
||||||
return ok(key)
|
Opt.some(key)
|
||||||
err()
|
else:
|
||||||
|
Opt.none(K)
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
|
||||||
proc firstKeyImpl[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc firstKeyImpl[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
return err()
|
Opt.none(K)
|
||||||
ok(rq.kFirst)
|
else:
|
||||||
|
Opt.some(rq.kFirst)
|
||||||
|
|
||||||
proc secondKeyImpl[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc secondKeyImpl[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
if rq.tab.len < 2:
|
if rq.tab.len < 2:
|
||||||
return err()
|
return Opt.none(K)
|
||||||
noKeyError("secondKeyImpl"):
|
noKeyError("secondKeyImpl"):
|
||||||
return ok(rq.tab[rq.kFirst].kNxt)
|
return Opt.some(rq.tab[rq.kFirst].kNxt)
|
||||||
|
|
||||||
proc beforeLastKeyImpl[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc beforeLastKeyImpl[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
if rq.tab.len < 2:
|
if rq.tab.len < 2:
|
||||||
return err()
|
return Opt.none(K)
|
||||||
noKeyError("lastKeyImpl"):
|
noKeyError("lastKeyImpl"):
|
||||||
return ok(rq.tab[rq.kLast].kPrv)
|
return Opt.some(rq.tab[rq.kLast].kPrv)
|
||||||
|
|
||||||
proc lastKeyImpl[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc lastKeyImpl[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
return err()
|
Opt.none(K)
|
||||||
ok(rq.kLast)
|
else:
|
||||||
|
Opt.some(rq.kLast)
|
||||||
|
|
||||||
proc nextKeyImpl[K,V](rq: var KeyedQueue[K,V]; key: K): Result[K,void] =
|
proc nextKeyImpl[K, V](rq: var KeyedQueue[K, V], key: K): Opt[K] =
|
||||||
if not rq.tab.hasKey(key) or rq.kLast == key:
|
if not rq.tab.hasKey(key) or rq.kLast == key:
|
||||||
return err()
|
return Opt.none(K)
|
||||||
noKeyError("nextKeyImpl"):
|
noKeyError("nextKeyImpl"):
|
||||||
return ok(rq.tab[key].kNxt)
|
return Opt.some(rq.tab[key].kNxt)
|
||||||
|
|
||||||
proc prevKeyImpl[K,V](rq: var KeyedQueue[K,V]; key: K): Result[K,void] =
|
proc prevKeyImpl[K, V](rq: var KeyedQueue[K, V], key: K): Opt[K] =
|
||||||
if not rq.tab.hasKey(key) or rq.kFirst == key:
|
if not rq.tab.hasKey(key) or rq.kFirst == key:
|
||||||
return err()
|
return Opt.none(K)
|
||||||
noKeyError("prevKeyImpl"):
|
noKeyError("prevKeyImpl"):
|
||||||
return ok(rq.tab[key].kPrv)
|
return Opt.some(rq.tab[key].kPrv)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions, constructor
|
# Public functions, constructor
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc init*[K,V](rq: var KeyedQueue[K,V]; initSize = 10) =
|
proc init*[K, V](rq: var KeyedQueue[K, V], initSize = defaultInitialSize) =
|
||||||
## Optional initaliser for the queue setting the inital size of the
|
## Optional initaliser for the queue setting the inital size of the
|
||||||
## underlying table object.
|
## underlying table object.
|
||||||
rq.tab = initTable[K,KeyedQueueItem[K,V]](initSize.nextPowerOfTwo)
|
rq.tab = initTable[K, KeyedQueueItem[K, V]](initSize)
|
||||||
|
|
||||||
proc init*[K,V](T: type KeyedQueue[K,V]; initSize = 10): T =
|
proc init*[K, V](T: type KeyedQueue[K, V], initSize = defaultInitialSize): T =
|
||||||
## Initaliser variant.
|
## Initaliser variant.
|
||||||
result.init(initSize)
|
result.init(initSize)
|
||||||
|
|
||||||
proc init*[K](rq: var KeyedQueueNV[K]; initSize = 10) =
|
proc init*[K](rq: var KeyedQueueNV[K], initSize = defaultInitialSize) =
|
||||||
## Key-only queue, no explicit values
|
## Key-only queue, no explicit values
|
||||||
rq.tab = initTable[K,KeyedQueueItem[K,BlindValue]](initSize.nextPowerOfTwo)
|
rq.tab = initTable[K, KeyedQueueItem[K, BlindValue]](initSize)
|
||||||
|
|
||||||
proc init*[K](T: type KeyedQueueNV[K]; initSize = 10): T =
|
proc init*[K](T: type KeyedQueueNV[K], initSize = defaultInitialSize): T =
|
||||||
## Initaliser variant.
|
## Initaliser variant.
|
||||||
result.init(initSize)
|
result.init(initSize)
|
||||||
|
|
||||||
|
@ -262,61 +265,62 @@ proc init*[K](T: type KeyedQueueNV[K]; initSize = 10): T =
|
||||||
# Public functions, list operations
|
# Public functions, list operations
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc append*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
proc append*[K, V](rq: var KeyedQueue[K, V], key: K, val: V): bool =
|
||||||
## Append new `key`. The function will succeed returning `true` unless the
|
## Append new `key`. The function will succeed returning `true` unless the
|
||||||
## `key` argument exists in the queue, already.
|
## `key` argument exists in the queue, already.
|
||||||
##
|
##
|
||||||
## All the items on the queue different from the one just added are
|
## All the items on the queue different from the one just added are
|
||||||
## called *previous* or *left hand* items while the item just added
|
## called *previous* or *left hand* items while the item just added
|
||||||
## is the *right-most* item.
|
## is the *right-most* item.
|
||||||
if not rq.tab.hasKey(key):
|
rq.tab.withValue(key, item):
|
||||||
|
return false
|
||||||
|
do:
|
||||||
rq.appendImpl(key, val)
|
rq.appendImpl(key, val)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
template push*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
template push*[K, V](rq: var KeyedQueue[K, V], key: K, val: V): bool =
|
||||||
## Same as `append()`
|
## Same as `append()`
|
||||||
rq.append(key, val)
|
rq.append(key, val)
|
||||||
|
|
||||||
|
proc replace*[K, V](rq: var KeyedQueue[K, V], key: K, val: V): bool =
|
||||||
proc replace*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
|
||||||
## Replace value for entry associated with the key argument `key`. Returns
|
## Replace value for entry associated with the key argument `key`. Returns
|
||||||
## `true` on success, and `false` otherwise.
|
## `true` on success, and `false` otherwise.
|
||||||
if rq.tab.hasKey(key):
|
rq.tab.withValue(key, item):
|
||||||
noKeyError("replace"):
|
item[].data = val
|
||||||
rq.tab[key].data = val
|
|
||||||
return true
|
return true
|
||||||
|
do:
|
||||||
|
return false
|
||||||
|
|
||||||
proc `[]=`*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V) =
|
proc `[]=`*[K, V](rq: var KeyedQueue[K, V], key: K, val: V) =
|
||||||
## This function provides a combined append/replace action with table
|
## This function provides a combined append/replace action with table
|
||||||
## semantics:
|
## semantics:
|
||||||
## * If the argument `key` is not in the queue yet, append the `(key,val)`
|
## * If the argument `key` is not in the queue yet, append the `(key,val)`
|
||||||
## pair as in `rq.append(key,val)`
|
## pair as in `rq.append(key,val)`
|
||||||
## * Otherwise replace the value entry of the queue item by the argument
|
## * Otherwise replace the value entry of the queue item by the argument
|
||||||
## `val` as in `rq.replace(key,val)`
|
## `val` as in `rq.replace(key,val)`
|
||||||
if rq.tab.hasKey(key):
|
rq.tab.withValue(key, item):
|
||||||
noKeyError("[]="):
|
item[].data = val
|
||||||
rq.tab[key].data = val
|
do:
|
||||||
else:
|
|
||||||
rq.appendImpl(key, val)
|
rq.appendImpl(key, val)
|
||||||
|
|
||||||
|
proc prepend*[K, V](rq: var KeyedQueue[K, V], key: K, val: V): bool =
|
||||||
proc prepend*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
|
||||||
## Prepend new `key`. The function will succeed returning `true` unless the
|
## Prepend new `key`. The function will succeed returning `true` unless the
|
||||||
## `key` argument exists in the queue, already.
|
## `key` argument exists in the queue, already.
|
||||||
##
|
##
|
||||||
## All the items on the queue different from the item just added are
|
## All the items on the queue different from the item just added are
|
||||||
## called *following* or *right hand* items while the item just added
|
## called *following* or *right hand* items while the item just added
|
||||||
## is the *left-most* item.
|
## is the *left-most* item.
|
||||||
if not rq.tab.hasKey(key):
|
rq.tab.withValue(key, item):
|
||||||
|
return false
|
||||||
|
do:
|
||||||
rq.prependImpl(key, val)
|
rq.prependImpl(key, val)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
template unshift*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
template unshift*[K, V](rq: var KeyedQueue[K, V], key: K, val: V): bool =
|
||||||
## Same as `prepend()`
|
## Same as `prepend()`
|
||||||
rq.prepend(key, val)
|
rq.prepend(key, val)
|
||||||
|
|
||||||
|
proc shift*[K, V](rq: var KeyedQueue[K, V]): Opt[KeyedQueuePair[K, V]] =
|
||||||
proc shift*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
|
||||||
## Deletes the *first* queue item and returns the key-value item pair just
|
## Deletes the *first* queue item and returns the key-value item pair just
|
||||||
## deleted. For a non-empty queue this function is the same as
|
## deleted. For a non-empty queue this function is the same as
|
||||||
## `rq.firstKey.value.delele`.
|
## `rq.firstKey.value.delele`.
|
||||||
|
@ -325,32 +329,26 @@ proc shift*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
||||||
## item returned and deleted is the *left-most* item.
|
## item returned and deleted is the *left-most* item.
|
||||||
type T = KeyedQueuePair[K, V]
|
type T = KeyedQueuePair[K, V]
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
noKeyError("shift"):
|
rq.tab.withValue(rq.kFirst, item):
|
||||||
let kvp = KeyedQueuePair[K,V](
|
let res = Opt.some KeyedQueuePair[K, V](key: rq.kFirst, data: move(item[].data))
|
||||||
key: rq.kFirst,
|
|
||||||
data: rq.tab[rq.kFirst].data)
|
|
||||||
rq.shiftImpl
|
rq.shiftImpl
|
||||||
when kvp is T:
|
return res
|
||||||
return ok(kvp)
|
Opt.none(KeyedQueuePair[K, V])
|
||||||
else:
|
|
||||||
return ok(T(kvp))
|
|
||||||
err()
|
|
||||||
|
|
||||||
proc shiftKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc shiftKey*[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
## Similar to `shift()` but with different return value.
|
## Similar to `shift()` but with different return value.
|
||||||
rq.shiftKeyImpl
|
rq.shiftKeyImpl
|
||||||
|
|
||||||
proc shiftValue*[K,V](rq: var KeyedQueue[K,V]): Result[V,void] =
|
proc shiftValue*[K, V](rq: var KeyedQueue[K, V]): Opt[V] =
|
||||||
## Similar to `shift()` but with different return value.
|
## Similar to `shift()` but with different return value.
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
noKeyError("shiftValue"):
|
rq.tab.withValue(rq.kFirst, item):
|
||||||
let val = rq.tab[rq.kFirst].data
|
let res = Opt.some(move(item[].data))
|
||||||
rq.shiftImpl
|
rq.shiftImpl
|
||||||
return ok(val)
|
return res
|
||||||
err()
|
Opt.none(V)
|
||||||
|
|
||||||
|
proc pop*[K, V](rq: var KeyedQueue[K, V]): Opt[KeyedQueuePair[K, V]] =
|
||||||
proc pop*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
|
||||||
## Deletes the *last* queue item and returns the key-value item pair just
|
## Deletes the *last* queue item and returns the key-value item pair just
|
||||||
## deleted. For a non-empty queue this function is the same as
|
## deleted. For a non-empty queue this function is the same as
|
||||||
## `rq.lastKey.value.delele`.
|
## `rq.lastKey.value.delele`.
|
||||||
|
@ -359,104 +357,84 @@ proc pop*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
||||||
## item returned and deleted is the *right-most* item.
|
## item returned and deleted is the *right-most* item.
|
||||||
type T = KeyedQueuePair[K, V]
|
type T = KeyedQueuePair[K, V]
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
noKeyError("pop"):
|
rq.tab.withValue(rq.kLast, item):
|
||||||
let kvp = KeyedQueuePair[K,V](
|
let res = Opt.some T(key: rq.kLast, data: move(item[].data))
|
||||||
key: rq.kLast,
|
|
||||||
data: rq.tab[rq.kLast].data)
|
|
||||||
rq.popImpl
|
rq.popImpl
|
||||||
when kvp is T:
|
return res
|
||||||
return ok(kvp)
|
Opt.none(T)
|
||||||
else:
|
|
||||||
return ok(T(kvp))
|
|
||||||
err()
|
|
||||||
|
|
||||||
proc popKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc popKey*[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
## Similar to `pop()` but with different return value.
|
## Similar to `pop()` but with different return value.
|
||||||
rq.popKeyImpl
|
rq.popKeyImpl
|
||||||
|
|
||||||
proc popValue*[K,V](rq: var KeyedQueue[K,V]): Result[V,void] =
|
proc popValue*[K, V](rq: var KeyedQueue[K, V]): Opt[V] =
|
||||||
## Similar to `pop()` but with different return value.
|
## Similar to `pop()` but with different return value.
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
noKeyError("popValue"):
|
rq.tab.withValue(rq.kLast, item):
|
||||||
let val = rq.tab[rq.kLast].data
|
let res = Opt.some move(item[].data)
|
||||||
rq.popImpl
|
rq.popImpl
|
||||||
return ok(val)
|
return res
|
||||||
err()
|
Opt.none(V)
|
||||||
|
|
||||||
|
proc delete*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[KeyedQueuePair[K, V]] =
|
||||||
proc delete*[K,V](rq: var KeyedQueue[K,V]; key: K):
|
|
||||||
Result[KeyedQueuePair[K,V], void] =
|
|
||||||
## Delete the item with key `key` from the queue and returns the key-value
|
## Delete the item with key `key` from the queue and returns the key-value
|
||||||
## item pair just deleted (if any).
|
## item pair just deleted (if any).
|
||||||
if rq.tab.hasKey(key):
|
type T = KeyedQueuePair[K, V]
|
||||||
noKeyError("delete"):
|
rq.tab.withValue(key, item):
|
||||||
let kvp = KeyedQueuePair[K,V](
|
let res = Opt.some T(key: key, data: move(item[].data))
|
||||||
key: key,
|
|
||||||
data: rq.tab[key].data)
|
|
||||||
rq.deleteImpl(key)
|
rq.deleteImpl(key)
|
||||||
return ok(kvp)
|
return res
|
||||||
err()
|
|
||||||
|
|
||||||
proc del*[K,V](rq: var KeyedQueue[K,V]; key: K) =
|
Opt.none(T)
|
||||||
|
|
||||||
|
proc del*[K, V](rq: var KeyedQueue[K, V], key: K) =
|
||||||
## Similar to `delete()` but without return code.
|
## Similar to `delete()` but without return code.
|
||||||
if rq.tab.hasKey(key):
|
if rq.tab.hasKey(key):
|
||||||
rq.deleteImpl(key)
|
rq.deleteImpl(key)
|
||||||
|
|
||||||
# --------
|
# --------
|
||||||
|
|
||||||
proc append*[K](rq: var KeyedQueueNV[K]; key: K): bool =
|
proc append*[K](rq: var KeyedQueueNV[K], key: K): bool =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.append(key,BlindValue(0))
|
rq.append(key, default(BlindValue))
|
||||||
|
|
||||||
template push*[K](rq: var KeyedQueueNV[K]; key: K): bool =
|
template push*[K](rq: var KeyedQueueNV[K], key: K): bool =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.append(key)
|
rq.append(key)
|
||||||
|
|
||||||
|
proc prepend*[K](rq: var KeyedQueueNV[K], key: K): bool =
|
||||||
proc prepend*[K](rq: var KeyedQueueNV[K]; key: K): bool =
|
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.prepend(key,BlindValue(0))
|
rq.prepend(key, default(BlindValue))
|
||||||
|
|
||||||
template unshift*[K](rq: var KeyedQueueNV[K]; key: K): bool =
|
template unshift*[K](rq: var KeyedQueueNV[K], key: K): bool =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.prepend(key)
|
rq.prepend(key)
|
||||||
|
|
||||||
|
proc shift*[K](rq: var KeyedQueueNV[K]): Opt[K] =
|
||||||
proc shift*[K](rq: var KeyedQueueNV[K]): Result[K,void] =
|
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.shiftKeyImpl
|
rq.shiftKeyImpl
|
||||||
|
|
||||||
proc shiftKey*[K](rq: var KeyedQueueNV[K]): Result[K,void]
|
proc pop*[K](rq: var KeyedQueueNV[K]): Opt[K] =
|
||||||
{.gcsafe, deprecated: "use shift() for key-only queue".} =
|
|
||||||
rq.shiftKeyImpl
|
|
||||||
|
|
||||||
|
|
||||||
proc pop*[K](rq: var KeyedQueueNV[K]): Result[K,void] =
|
|
||||||
## Key-only variant of `pop()` (same as `popKey()`)
|
## Key-only variant of `pop()` (same as `popKey()`)
|
||||||
rq.popKeyImpl
|
rq.popKeyImpl
|
||||||
|
|
||||||
proc popKey*[K](rq: var KeyedQueueNV[K]): Result[K,void]
|
|
||||||
{.gcsafe, deprecated: "use pop() for key-only queue".} =
|
|
||||||
rq.popKeyImpl
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions, fetch
|
# Public functions, fetch
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc hasKey*[K,V](rq: var KeyedQueue[K,V]; key: K): bool =
|
proc hasKey*[K, V](rq: var KeyedQueue[K, V], key: K): bool =
|
||||||
## Check whether the argument `key` has been queued, already
|
## Check whether the argument `key` has been queued, already
|
||||||
rq.tab.hasKey(key)
|
rq.tab.hasKey(key)
|
||||||
|
|
||||||
proc eq*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[V,void] =
|
proc eq*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[V] =
|
||||||
## Retrieve the value data stored with the argument `key` from
|
## Retrieve the value data stored with the argument `key` from
|
||||||
## the queue if there is any.
|
## the queue if there is any.
|
||||||
if not rq.tab.hasKey(key):
|
if not rq.tab.hasKey(key):
|
||||||
return err()
|
return Opt.none(V)
|
||||||
noKeyError("eq"):
|
noKeyError("eq"):
|
||||||
return ok(rq.tab[key].data)
|
return Opt.some(rq.tab[key].data)
|
||||||
|
|
||||||
proc `[]`*[K,V](rq: var KeyedQueue[K,V]; key: K): V
|
proc `[]`*[K, V](rq: var KeyedQueue[K, V], key: K): var V {.raises: [KeyError].} =
|
||||||
{.gcsafe,raises: [KeyError].} =
|
|
||||||
## This function provides a simplified version of the `eq()` function with
|
## This function provides a simplified version of the `eq()` function with
|
||||||
## table semantics. Note that this finction throws a `KeyError` exception
|
## table semantics. Note that this finction throws a `KeyError` exception
|
||||||
## unless the argument `key` exists in the queue.
|
## unless the argument `key` exists in the queue.
|
||||||
|
@ -466,38 +444,39 @@ proc `[]`*[K,V](rq: var KeyedQueue[K,V]; key: K): V
|
||||||
# Public functions, LRU mode
|
# Public functions, LRU mode
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc lruFetch*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[V,void] =
|
proc lruFetch*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[V] =
|
||||||
## Fetch in *last-recently-used* mode: If the argument `key` exists in the
|
## Fetch in *last-recently-used* mode: If the argument `key` exists in the
|
||||||
## queue, move the key-value item pair to the *right end* (see `append()`)
|
## queue, move the key-value item pair to the *right end* (see `append()`)
|
||||||
## of the queue and return the value associated with the key.
|
## of the queue and return the value associated with the key.
|
||||||
if not rq.tab.hasKey(key):
|
rq.tab.withValue(key, item):
|
||||||
return err()
|
|
||||||
|
|
||||||
noKeyError("lruFetch"):
|
|
||||||
let item = rq.tab[key]
|
|
||||||
if rq.kLast != key:
|
if rq.kLast != key:
|
||||||
# Now, `key` is in the table and does not refer to the last `item`,
|
# Now, `key` is in the table and does not refer to the last `item`,
|
||||||
# so the table has at least two entries.
|
# so the table has at least two entries.
|
||||||
|
|
||||||
# unlink item
|
# unlink item
|
||||||
if rq.kFirst == key:
|
if rq.kFirst == key:
|
||||||
rq.kFirst = item.kNxt
|
rq.kFirst = item[].kNxt
|
||||||
rq.tab[rq.kFirst].kPrv = rq.tab[rq.kFirst].kNxt # term node: nxt == prv
|
rq.tab.withValue(rq.kFirst, first):
|
||||||
|
first[].kPrv = first[].kNxt # term node: nxt == prv
|
||||||
else: # Now, there are at least three entries
|
else: # Now, there are at least three entries
|
||||||
if rq.tab[rq.kFirst].kNxt == key:
|
rq.tab.withValue(rq.kFirst, first):
|
||||||
rq.tab[rq.kFirst].kPrv = item.kNxt # item was the 2nd one
|
if first[].kNxt == key:
|
||||||
rq.tab[item.kPrv].kNxt = item.kNxt
|
first[].kPrv = item[].kNxt # item was the 2nd one
|
||||||
rq.tab[item.kNxt].kPrv = item.kPrv
|
rq.tab.withValue(item[].kPrv, prev):
|
||||||
|
prev[].kNxt = item[].kNxt
|
||||||
|
rq.tab.withValue(item[].kNxt, next):
|
||||||
|
next[].kPrv = item[].kPrv
|
||||||
|
|
||||||
# Re-append item, i.e. appendImpl() without adding item.
|
# Re-append item, i.e. appendImpl() without adding item.
|
||||||
rq.tab[rq.kLast].kNxt = key
|
rq.tab.withValue(rq.kLast, last):
|
||||||
rq.tab[key].kPrv = rq.kLast
|
last[].kNxt = key
|
||||||
rq.kLast = key
|
|
||||||
rq.tab[key].kNxt = rq.tab[key].kPrv # term node: nxt == prv
|
|
||||||
return ok(item.data)
|
|
||||||
|
|
||||||
proc lruUpdate*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
item[].kPrv = rq.kLast
|
||||||
|
rq.kLast = key
|
||||||
|
item[].kNxt = item[].kPrv # term node: nxt == prv
|
||||||
|
return Opt.some(item[].data)
|
||||||
|
|
||||||
|
proc lruUpdate*[K, V](rq: var KeyedQueue[K, V], key: K, val: V): bool =
|
||||||
## Similar to `lruFetch()` with the difference that the item value is
|
## Similar to `lruFetch()` with the difference that the item value is
|
||||||
## updated (i.e. set to `val`) if it is found on the queue. In that case,
|
## updated (i.e. set to `val`) if it is found on the queue. In that case,
|
||||||
## `true` is returned.
|
## `true` is returned.
|
||||||
|
@ -505,11 +484,11 @@ proc lruUpdate*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V): bool =
|
||||||
## Otherwise `false` is returned.
|
## Otherwise `false` is returned.
|
||||||
##
|
##
|
||||||
rq.tab.withValue(key, w):
|
rq.tab.withValue(key, w):
|
||||||
w.data = val
|
w[].data = val
|
||||||
discard rq.lruFetch key
|
discard rq.lruFetch key
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc lruAppend*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V; maxItems: int): V =
|
proc lruAppend*[K, V](rq: var KeyedQueue[K, V], key: K, val: V, maxItems: int): V =
|
||||||
## Append in *last-recently-used* mode: If the queue has at least `maxItems`
|
## Append in *last-recently-used* mode: If the queue has at least `maxItems`
|
||||||
## item entries, do `shift()` out the *left-most* one. Then `append()` the
|
## item entries, do `shift()` out the *left-most* one. Then `append()` the
|
||||||
## key-value argument pair `(key,val)` to the *right end*. Together with
|
## key-value argument pair `(key,val)` to the *right end*. Together with
|
||||||
|
@ -527,12 +506,12 @@ proc lruAppend*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V; maxItems: int): V
|
||||||
## block:
|
## block:
|
||||||
## let rc = q.lruFetch(key)
|
## let rc = q.lruFetch(key)
|
||||||
## if rc.isOK:
|
## if rc.isOK:
|
||||||
## return ok(rc.value)
|
## return Opt.some(rc.value)
|
||||||
## block:
|
## block:
|
||||||
## let rc = expensiveCalculation(key)
|
## let rc = expensiveCalculation(key)
|
||||||
## if rc.isOK:
|
## if rc.isOK:
|
||||||
## return ok(q.lruAppend(key, rc.value, queueMax))
|
## return Opt.some(q.lruAppend(key, rc.value, queueMax))
|
||||||
## err()
|
## Opt.none(K)
|
||||||
##
|
##
|
||||||
## Caveat:
|
## Caveat:
|
||||||
## This fuction must always be used in combination with `lruFetch()` or
|
## This fuction must always be used in combination with `lruFetch()` or
|
||||||
|
@ -542,7 +521,6 @@ proc lruAppend*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V; maxItems: int): V
|
||||||
# Make sure that there is no such key. Otherwise the optimised `appendImpl()`
|
# Make sure that there is no such key. Otherwise the optimised `appendImpl()`
|
||||||
# function might garble the list of links.
|
# function might garble the list of links.
|
||||||
doAssert not rq.tab.hasKey key
|
doAssert not rq.tab.hasKey key
|
||||||
|
|
||||||
# Limit number of cached items
|
# Limit number of cached items
|
||||||
try:
|
try:
|
||||||
if maxItems <= rq.tab.len:
|
if maxItems <= rq.tab.len:
|
||||||
|
@ -557,35 +535,35 @@ proc lruAppend*[K,V](rq: var KeyedQueue[K,V]; key: K; val: V; maxItems: int): V
|
||||||
# Public traversal functions, fetch keys
|
# Public traversal functions, fetch keys
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc firstKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc firstKey*[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
## Retrieve first key from the queue unless it is empty.
|
## Retrieve first key from the queue unless it is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## key returned is the *left-most* one.
|
## key returned is the *left-most* one.
|
||||||
rq.firstKeyImpl
|
rq.firstKeyImpl
|
||||||
|
|
||||||
proc secondKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc secondKey*[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
## Retrieve the key next after the first key from queue unless it is empty.
|
## Retrieve the key next after the first key from queue unless it is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## key returned is the one ti the right of the *left-most* one.
|
## key returned is the one ti the right of the *left-most* one.
|
||||||
rq.secondKeyImpl
|
rq.secondKeyImpl
|
||||||
|
|
||||||
proc beforeLastKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc beforeLastKey*[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
## Retrieve the key just before the last one from queue unless it is empty.
|
## Retrieve the key just before the last one from queue unless it is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## key returned is the one to the left of the *right-most* one.
|
## key returned is the one to the left of the *right-most* one.
|
||||||
rq.beforeLastKeyImpl
|
rq.beforeLastKeyImpl
|
||||||
|
|
||||||
proc lastKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc lastKey*[K, V](rq: var KeyedQueue[K, V]): Opt[K] =
|
||||||
## Retrieve last key from queue unless it is empty.
|
## Retrieve last key from queue unless it is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## key returned is the *right-most* one.
|
## key returned is the *right-most* one.
|
||||||
rq.lastKeyImpl
|
rq.lastKeyImpl
|
||||||
|
|
||||||
proc nextKey*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[K,void] =
|
proc nextKey*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[K] =
|
||||||
## Retrieve the key following the argument `key` from queue if
|
## Retrieve the key following the argument `key` from queue if
|
||||||
## there is any.
|
## there is any.
|
||||||
##
|
##
|
||||||
|
@ -593,7 +571,7 @@ proc nextKey*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[K,void] =
|
||||||
## key returned is the next one to the *right*.
|
## key returned is the next one to the *right*.
|
||||||
rq.nextKeyImpl(key)
|
rq.nextKeyImpl(key)
|
||||||
|
|
||||||
proc prevKey*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[K,void] =
|
proc prevKey*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[K] =
|
||||||
## Retrieve the key preceeding the argument `key` from queue if
|
## Retrieve the key preceeding the argument `key` from queue if
|
||||||
## there is any.
|
## there is any.
|
||||||
##
|
##
|
||||||
|
@ -603,108 +581,111 @@ proc prevKey*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[K,void] =
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
|
|
||||||
proc firstKey*[K](rq: var KeyedQueueNV[K]): Result[K,void]
|
proc firstKey*[K](
|
||||||
{.gcsafe, deprecated: "use first() for key-only queue".} =
|
rq: var KeyedQueueNV[K]
|
||||||
|
): Opt[K] {.gcsafe, deprecated: "use first() for key-only queue".} =
|
||||||
rq.firstKeyImpl
|
rq.firstKeyImpl
|
||||||
|
|
||||||
proc secondKey*[K](rq: var KeyedQueueNV[K]): Result[K,void]
|
proc secondKey*[K](
|
||||||
{.gcsafe, deprecated: "use second() for key-only queue".} =
|
rq: var KeyedQueueNV[K]
|
||||||
|
): Opt[K] {.gcsafe, deprecated: "use second() for key-only queue".} =
|
||||||
rq.secondKeyImpl
|
rq.secondKeyImpl
|
||||||
|
|
||||||
proc beforeLastKey*[K](rq: var KeyedQueueNV[K]): Result[K,void]
|
proc beforeLastKey*[K](
|
||||||
{.gcsafe, deprecated: "use beforeLast() for key-only queue".} =
|
rq: var KeyedQueueNV[K]
|
||||||
|
): Opt[K] {.gcsafe, deprecated: "use beforeLast() for key-only queue".} =
|
||||||
rq.beforeLastKeyImpl
|
rq.beforeLastKeyImpl
|
||||||
|
|
||||||
proc lastKey*[K](rq: var KeyedQueueNV[K]): Result[K,void]
|
proc lastKey*[K](
|
||||||
{.gcsafe, deprecated: "use last() for key-only queue".} =
|
rq: var KeyedQueueNV[K]
|
||||||
|
): Opt[K] {.gcsafe, deprecated: "use last() for key-only queue".} =
|
||||||
rq.lastKeyImpl
|
rq.lastKeyImpl
|
||||||
|
|
||||||
proc nextKey*[K](rq: var KeyedQueueNV[K]; key: K): Result[K,void]
|
proc nextKey*[K](
|
||||||
{.gcsafe, deprecated: "use next() for key-only queue".} =
|
rq: var KeyedQueueNV[K], key: K
|
||||||
|
): Opt[K] {.gcsafe, deprecated: "use next() for key-only queue".} =
|
||||||
rq.nextKeyImpl(key)
|
rq.nextKeyImpl(key)
|
||||||
|
|
||||||
proc prevKey*[K](rq: var KeyedQueueNV[K]; key: K): Result[K,void]
|
proc prevKey*[K](
|
||||||
{.gcsafe, deprecated: "use prev() for key-only queue".} =
|
rq: var KeyedQueueNV[K], key: K
|
||||||
|
): Opt[K] {.gcsafe, deprecated: "use prev() for key-only queue".} =
|
||||||
rq.nextKeyImpl(key)
|
rq.nextKeyImpl(key)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public traversal functions, fetch key/value pairs
|
# Public traversal functions, fetch key/value pairs
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc first*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
proc first*[K, V](rq: var KeyedQueue[K, V]): Opt[KeyedQueuePair[K, V]] =
|
||||||
## Similar to `firstKey()` but with key-value item pair return value.
|
## Similar to `firstKey()` but with key-value item pair return value.
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
return err()
|
return Opt.none(KeyedQueuePair[K, V])
|
||||||
noKeyError("first"):
|
noKeyError("first"):
|
||||||
let key = rq.kFirst
|
let key = rq.kFirst
|
||||||
return ok(KeyedQueuePair[K,V](key: key, data: rq.tab[key].data))
|
return Opt.some(KeyedQueuePair[K, V](key: key, data: rq.tab[key].data))
|
||||||
|
|
||||||
proc second*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
proc second*[K, V](rq: var KeyedQueue[K, V]): Opt[KeyedQueuePair[K, V]] =
|
||||||
## Similar to `secondKey()` but with key-value item pair return value.
|
## Similar to `secondKey()` but with key-value item pair return value.
|
||||||
if rq.tab.len < 2:
|
if rq.tab.len < 2:
|
||||||
return err()
|
return Opt.none(KeyedQueuePair[K, V])
|
||||||
noKeyError("second"):
|
noKeyError("second"):
|
||||||
let key = rq.tab[rq.kFirst].kNxt
|
let key = rq.tab[rq.kFirst].kNxt
|
||||||
return ok(KeyedQueuePair[K,V](key: key, data: rq.tab[key].data))
|
return Opt.some(KeyedQueuePair[K, V](key: key, data: rq.tab[key].data))
|
||||||
|
|
||||||
proc beforeLast*[K,V](rq: var KeyedQueue[K,V]):
|
proc beforeLast*[K, V](rq: var KeyedQueue[K, V]): Opt[KeyedQueuePair[K, V]] =
|
||||||
Result[KeyedQueuePair[K,V],void] =
|
|
||||||
## Similar to `beforeLastKey()` but with key-value item pair return value.
|
## Similar to `beforeLastKey()` but with key-value item pair return value.
|
||||||
if rq.tab.len < 2:
|
if rq.tab.len < 2:
|
||||||
return err()
|
return Opt.none(KeyedQueuePair[K, V])
|
||||||
noKeyError("beforeLast"):
|
noKeyError("beforeLast"):
|
||||||
let key = rq.tab[rq.kLast].kPrv
|
let key = rq.tab[rq.kLast].kPrv
|
||||||
return ok(KeyedQueuePair[K,V](key: key, data: rq.tab[key].data))
|
return Opt.some(KeyedQueuePair[K, V](key: key, data: rq.tab[key].data))
|
||||||
|
|
||||||
proc last*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
proc last*[K, V](rq: var KeyedQueue[K, V]): Opt[KeyedQueuePair[K, V]] =
|
||||||
## Similar to `lastKey()` but with key-value item pair return value.
|
## Similar to `lastKey()` but with key-value item pair return value.
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
return err()
|
return Opt.none(KeyedQueuePair[K, V])
|
||||||
noKeyError("last"):
|
noKeyError("last"):
|
||||||
let key = rq.kLast
|
let key = rq.kLast
|
||||||
return ok(KeyedQueuePair[K,V](key: key, data: rq.tab[key].data))
|
return Opt.some(KeyedQueuePair[K, V](key: key, data: rq.tab[key].data))
|
||||||
|
|
||||||
proc next*[K,V](rq: var KeyedQueue[K,V]; key: K):
|
proc next*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[KeyedQueuePair[K, V]] =
|
||||||
Result[KeyedQueuePair[K,V],void] =
|
|
||||||
## Similar to `nextKey()` but with key-value item pair return value.
|
## Similar to `nextKey()` but with key-value item pair return value.
|
||||||
if not rq.tab.hasKey(key) or rq.kLast == key:
|
if not rq.tab.hasKey(key) or rq.kLast == key:
|
||||||
return err()
|
return Opt.none(KeyedQueuePair[K, V])
|
||||||
noKeyError("next"):
|
noKeyError("next"):
|
||||||
let nKey = rq.tab[key].kNxt
|
let nKey = rq.tab[key].kNxt
|
||||||
return ok(KeyedQueuePair[K,V](key: nKey, data: rq.tab[nKey].data))
|
return Opt.some(KeyedQueuePair[K, V](key: nKey, data: rq.tab[nKey].data))
|
||||||
|
|
||||||
proc prev*[K,V](rq: var KeyedQueue[K,V]; key: K):
|
proc prev*[K, V](rq: var KeyedQueue[K, V], key: K): Opt[KeyedQueuePair[K, V]] =
|
||||||
Result[KeyedQueuePair[K,V],void] =
|
|
||||||
## Similar to `prevKey()` but with key-value item pair return value.
|
## Similar to `prevKey()` but with key-value item pair return value.
|
||||||
if not rq.tab.hasKey(key) or rq.kFirst == key:
|
if not rq.tab.hasKey(key) or rq.kFirst == key:
|
||||||
return err()
|
return Opt.none(KeyedQueuePair[K, V])
|
||||||
noKeyError("prev"):
|
noKeyError("prev"):
|
||||||
let pKey = rq.tab[key].kPrv
|
let pKey = rq.tab[key].kPrv
|
||||||
return ok(KeyedQueuePair[K,V](key: pKey, data: rq.tab[pKey].data))
|
return Opt.some(KeyedQueuePair[K, V](key: pKey, data: rq.tab[pKey].data))
|
||||||
|
|
||||||
# ------------
|
# ------------
|
||||||
|
|
||||||
proc first*[K](rq: var KeyedQueueNV[K]): Result[K,void] =
|
proc first*[K](rq: var KeyedQueueNV[K]): Opt[K] =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.firstKeyImpl
|
rq.firstKeyImpl
|
||||||
|
|
||||||
proc second*[K](rq: var KeyedQueueNV[K]): Result[K,void] =
|
proc second*[K](rq: var KeyedQueueNV[K]): Opt[K] =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.secondKeyImpl
|
rq.secondKeyImpl
|
||||||
|
|
||||||
proc beforeLast*[K](rq: var KeyedQueueNV[K]): Result[K,void] =
|
proc beforeLast*[K](rq: var KeyedQueueNV[K]): Opt[K] =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.beforeLastKeyImpl
|
rq.beforeLastKeyImpl
|
||||||
|
|
||||||
proc last*[K](rq: var KeyedQueueNV[K]): Result[K,void] =
|
proc last*[K](rq: var KeyedQueueNV[K]): Opt[K] =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.lastKeyImpl
|
rq.lastKeyImpl
|
||||||
|
|
||||||
proc next*[K](rq: var KeyedQueueNV[K]; key: K): Result[K,void] =
|
proc next*[K](rq: var KeyedQueueNV[K], key: K): Opt[K] =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.nextKeyImpl(key)
|
rq.nextKeyImpl(key)
|
||||||
|
|
||||||
proc prev*[K](rq: var KeyedQueueNV[K]; key: K): Result[K,void] =
|
proc prev*[K](rq: var KeyedQueueNV[K], key: K): Opt[K] =
|
||||||
## Key-only queue variant
|
## Key-only queue variant
|
||||||
rq.nextKeyImpl(key)
|
rq.nextKeyImpl(key)
|
||||||
|
|
||||||
|
@ -712,47 +693,47 @@ proc prev*[K](rq: var KeyedQueueNV[K]; key: K): Result[K,void] =
|
||||||
# Public traversal functions, data container items
|
# Public traversal functions, data container items
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc firstValue*[K,V](rq: var KeyedQueue[K,V]): Result[V,void] =
|
proc firstValue*[K, V](rq: var KeyedQueue[K, V]): Opt[V] =
|
||||||
## Retrieve first value item from the queue unless it is empty.
|
## Retrieve first value item from the queue unless it is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## value item returned is the *left-most* one.
|
## value item returned is the *left-most* one.
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
return err()
|
return Opt.none(V)
|
||||||
noKeyError("firstValue"):
|
noKeyError("firstValue"):
|
||||||
return ok(rq.tab[rq.kFirst].data)
|
return Opt.some(rq.tab[rq.kFirst].data)
|
||||||
|
|
||||||
proc secondValue*[K,V](rq: var KeyedQueue[K,V]): Result[V,void] =
|
proc secondValue*[K, V](rq: var KeyedQueue[K, V]): Opt[V] =
|
||||||
## Retrieve the value item next to the first one from the queue unless it
|
## Retrieve the value item next to the first one from the queue unless it
|
||||||
## is empty.
|
## is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## value item returned is the one to the *right* of the *left-most* one.
|
## value item returned is the one to the *right* of the *left-most* one.
|
||||||
if rq.tab.len < 2:
|
if rq.tab.len < 2:
|
||||||
return err()
|
return Opt.none(K)
|
||||||
noKeyError("secondValue"):
|
noKeyError("secondValue"):
|
||||||
return ok(rq.tab[rq.tab[rq.kFirst].kNxt].data)
|
return Opt.some(rq.tab[rq.tab[rq.kFirst].kNxt].data)
|
||||||
|
|
||||||
proc beforeLastValue*[K,V](rq: var KeyedQueue[K,V]): Result[V,void] =
|
proc beforeLastValue*[K, V](rq: var KeyedQueue[K, V]): Opt[V] =
|
||||||
## Retrieve the value item just before the last item from the queue
|
## Retrieve the value item just before the last item from the queue
|
||||||
## unless it is empty.
|
## unless it is empty.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## value item returned is the one to the *left* of the *right-most* one.
|
## value item returned is the one to the *left* of the *right-most* one.
|
||||||
if rq.tab.len < 2:
|
if rq.tab.len < 2:
|
||||||
return err()
|
return Opt.none(V)
|
||||||
noKeyError("beforeLastValue"):
|
noKeyError("beforeLastValue"):
|
||||||
return ok(rq.tab[rq.tab[rq.kLast].kPrv].data)
|
return Opt.some(rq.tab[rq.tab[rq.kLast].kPrv].data)
|
||||||
|
|
||||||
proc lastValue*[K,V](rq: var KeyedQueue[K,V]): Result[V,void] =
|
proc lastValue*[K, V](rq: var KeyedQueue[K, V]): Opt[V] =
|
||||||
## Retrieve the last value item from the queue if there is any.
|
## Retrieve the last value item from the queue if there is any.
|
||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## value item returned is the *right-most* one.
|
## value item returned is the *right-most* one.
|
||||||
if rq.tab.len == 0:
|
if rq.tab.len == 0:
|
||||||
return err()
|
return Opt.none(V)
|
||||||
noKeyError("lastValue"):
|
noKeyError("lastValue"):
|
||||||
return ok(rq.tab[rq.kLast].data)
|
return Opt.some(rq.tab[rq.kLast].data)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions, miscellaneous
|
# Public functions, miscellaneous
|
||||||
|
@ -786,10 +767,9 @@ proc clear*[K,V](rq: var KeyedQueue[K,V]) =
|
||||||
rq.kFirst.reset
|
rq.kFirst.reset
|
||||||
rq.kLast.reset
|
rq.kLast.reset
|
||||||
|
|
||||||
proc toKeyedQueueResult*[K,V](key: K; data: V):
|
proc toKeyedQueueResult*[K, V](key: K, data: V): Opt[KeyedQueuePair[K, V]] =
|
||||||
Result[KeyedQueuePair[K,V],void] =
|
## Helper, chreate `Opt.some()` result
|
||||||
## Helper, chreate `ok()` result
|
Opt.some(KeyedQueuePair[K, V](key: key, data: data))
|
||||||
ok(KeyedQueuePair[K,V](key: key, data: data))
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public iterators
|
# Public iterators
|
||||||
|
|
Loading…
Reference in New Issue