137 lines
4.3 KiB
Nim
137 lines
4.3 KiB
Nim
# stew
|
|
# Copyright 2024 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
#
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
#
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
import ./[evalonce, arrayops]
|
|
|
|
type ArrayBuf*[N: static int, T] = object
|
|
## An fixed-capacity, allocation-free buffer with a seq-like API - suitable
|
|
## for keeping small amounts of data since the full capacity is reserved on
|
|
## instantiation (using an `array`).
|
|
#
|
|
# `N` must be "simple enough" or one of these will trigger
|
|
# TODO https://github.com/nim-lang/Nim/issues/24043
|
|
# TODO https://github.com/nim-lang/Nim/issues/24044
|
|
# TODO https://github.com/nim-lang/Nim/issues/24045
|
|
buf*: array[N, T]
|
|
|
|
when sizeof(int) > sizeof(uint8):
|
|
when N <= int(uint8.high):
|
|
n*: uint8
|
|
else:
|
|
when sizeof(int) > sizeof(uint16):
|
|
when N <= int(uint16.high):
|
|
n*: uint16
|
|
else:
|
|
when sizeof(int) > sizeof(uint32):
|
|
# TODO https://github.com/nim-lang/Nim/issues/24041
|
|
when N <= cast[int](uint32.high):
|
|
n*: uint32
|
|
else:
|
|
n*: int
|
|
else:
|
|
n*: int
|
|
else:
|
|
n*: int
|
|
else:
|
|
n*: int
|
|
# Number of entries actually in use - uses the smallest unsigned integer
|
|
# that can hold values up to the capacity to avoid wasting memory on
|
|
# alignment and counting, specially when `T = byte` and odd sizes are used
|
|
|
|
template len*(b: ArrayBuf): int =
|
|
int(b.n)
|
|
|
|
template setLen*(b: var ArrayBuf, newLenParam: int) =
|
|
block:
|
|
newLenParam.evalOnceAs(newLen)
|
|
let nl = typeof(b.n)(newLen)
|
|
for i in newLen ..< b.len():
|
|
reset(b.buf[i]) # reset cleared items when shrinking
|
|
b.n = nl
|
|
|
|
template data*(bParam: ArrayBuf): openArray =
|
|
# TODO https://github.com/nim-lang/Nim/issues/24260
|
|
# TODO https://github.com/nim-lang/Nim/issues/24261
|
|
bParam.evalOnceAs(bArrayBufPrivate)
|
|
bArrayBufPrivate.buf.toOpenArray(0, bArrayBufPrivate.len() - 1)
|
|
|
|
template data*(bParam: var ArrayBuf): var openArray =
|
|
# TODO https://github.com/nim-lang/Nim/issues/24260
|
|
# TODO https://github.com/nim-lang/Nim/issues/24261
|
|
bParam.evalOnceAs(bArrayBufPrivate)
|
|
bArrayBufPrivate.buf.toOpenArray(0, bArrayBufPrivate.len() - 1)
|
|
|
|
iterator items*[N, T](b: ArrayBuf[N, T]): lent T =
|
|
for i in 0 ..< b.len:
|
|
yield b.buf[i]
|
|
|
|
iterator mitems*[N, T](b: var ArrayBuf[N, T]): var T =
|
|
for i in 0 ..< b.len:
|
|
yield b.d[i]
|
|
|
|
iterator pairs*[N, T](b: ArrayBuf[N, T]): (int, lent T) =
|
|
for i in 0 ..< b.len:
|
|
yield (i, b.buf[i])
|
|
|
|
template `[]`*[N, T](b: ArrayBuf[N, T], i: int): lent T =
|
|
b.buf[i]
|
|
|
|
template `[]`*[N, T](b: var ArrayBuf[N, T], i: int): var T =
|
|
b.buf[i]
|
|
|
|
template `[]=`*[N, T](b: var ArrayBuf[N, T], i: int, v: T) =
|
|
b.buf[i] = v
|
|
|
|
template `[]`*[N, T](b: ArrayBuf[N, T], i: BackwardsIndex): lent T =
|
|
b.buf[b.len - int(i)]
|
|
|
|
template `==`*(a, b: ArrayBuf): bool =
|
|
a.data() == b.data()
|
|
|
|
template `<`*(a, b: ArrayBuf): bool =
|
|
a.data() < b.data()
|
|
|
|
template initCopyFrom*[N, T](
|
|
_: type ArrayBuf[N, T], data: openArray[T]
|
|
): ArrayBuf[N, T] =
|
|
var v: ArrayBuf[N, T]
|
|
v.n = typeof(v.n)(v.buf.copyFrom(data))
|
|
v
|
|
|
|
template initCopyFrom*[N, T](
|
|
_: type ArrayBuf[N, T], data: array[N, T]
|
|
): ArrayBuf[N, T] =
|
|
# Shortcut version that avoids zeroMem on matching lengths
|
|
ArrayBuf[N, T](
|
|
buf: data,
|
|
n: N
|
|
)
|
|
|
|
template add*[N, T](b: var ArrayBuf[N, T], v: T) =
|
|
## Adds items up to capacity then drops the rest
|
|
# TODO `b` is evaluated multiple times but since it's a `var` this should
|
|
# _hopefully_ be fine..
|
|
if b.len < N:
|
|
b.buf[b.len] = v
|
|
b.n += 1
|
|
|
|
template add*[N, T](b: var ArrayBuf[N, T], v: openArray[T]) =
|
|
## Adds items up to capacity then drops the rest
|
|
# TODO `b` is evaluated multiple times but since it's a `var` this should
|
|
# _hopefully_ be fine..
|
|
b.n += typeof(b.n)(b.buf.toOpenArray(b.len, N - 1).copyFrom(v))
|
|
|
|
template pop*[N, T](b: var ArrayBuf[N, T]): T =
|
|
## Return the last item while removing it from the buffer
|
|
# TODO `b` is evaluated multiple times but since it's a `var` this should
|
|
# _hopefully_ be fine..
|
|
assert b.n > 0, "pop from empty ArrayBuf"
|
|
b.n -= 1
|
|
move(b.buf[b.n])
|