2024-01-18 11:07:03 +00:00
|
|
|
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
2021-12-10 10:12:24 +00:00
|
|
|
# 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.
|
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
{.push raises: [].}
|
|
|
|
|
2021-10-15 11:38:51 +00:00
|
|
|
import
|
2022-07-09 08:55:15 +00:00
|
|
|
std/[options, math]
|
2021-10-15 11:38:51 +00:00
|
|
|
|
2024-01-18 11:07:03 +00:00
|
|
|
from stew/assign2 import assign
|
|
|
|
|
2021-10-15 11:38:51 +00:00
|
|
|
export options
|
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
# Buffer implementation similar to the one in the reference implementation.
|
|
|
|
# Main rationale for it is to refer to items in buffer by their sequence number
|
|
|
|
# and to support out of order packets.
|
|
|
|
# Therefore it is a super specific data structure, and it mostly usefully for
|
|
|
|
# the uTP implementation.
|
|
|
|
# An alternative could be to use standard deque from deques module, and
|
|
|
|
# calculate item indexes from their sequence numbers.
|
2021-12-20 12:14:50 +00:00
|
|
|
type
|
2021-10-25 07:58:13 +00:00
|
|
|
GrowableCircularBuffer*[A] = object
|
|
|
|
items: seq[Option[A]]
|
2022-03-24 14:56:00 +00:00
|
|
|
mask: uint32
|
2021-10-15 11:38:51 +00:00
|
|
|
|
|
|
|
# provided size will always be adjusted to next power of two
|
2022-03-24 14:56:00 +00:00
|
|
|
proc init*[A](T: type GrowableCircularBuffer[A], size: uint32 = 16): T =
|
|
|
|
let powOfTwoSize = nextPowerOfTwo(int(size))
|
2021-10-15 11:38:51 +00:00
|
|
|
T(
|
2022-03-24 14:56:00 +00:00
|
|
|
items: newSeq[Option[A]](powOfTwoSize),
|
|
|
|
mask: uint32(powOfTwoSize - 1)
|
2021-10-15 11:38:51 +00:00
|
|
|
)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2022-03-24 14:56:00 +00:00
|
|
|
proc get*[A](buff: GrowableCircularBuffer[A], i: uint32): Option[A] =
|
2024-01-18 11:07:03 +00:00
|
|
|
assign(result, buff.items[i and buff.mask])
|
2021-10-15 11:38:51 +00:00
|
|
|
|
2022-03-24 14:56:00 +00:00
|
|
|
proc putImpl[A](buff: var GrowableCircularBuffer[A], i: uint32, elem: Option[A]) =
|
2021-10-15 11:38:51 +00:00
|
|
|
buff.items[i and buff.mask] = elem
|
|
|
|
|
2022-03-24 14:56:00 +00:00
|
|
|
proc put*[A](buff: var GrowableCircularBuffer[A], i: uint32, elem: A) =
|
2021-10-15 11:38:51 +00:00
|
|
|
buff.putImpl(i, some(elem))
|
|
|
|
|
2022-03-24 14:56:00 +00:00
|
|
|
proc delete*[A](buff: var GrowableCircularBuffer[A], i: uint32) =
|
2021-10-15 11:38:51 +00:00
|
|
|
buff.putImpl(i, none[A]())
|
|
|
|
|
2022-03-24 14:56:00 +00:00
|
|
|
proc hasKey*[A](buff: GrowableCircularBuffer[A], i: uint32): bool =
|
2021-10-25 07:58:13 +00:00
|
|
|
buff.get(i).isSome()
|
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
proc exists*[A](
|
|
|
|
buff: GrowableCircularBuffer[A], i: uint32,
|
|
|
|
check: proc (x: A): bool {.gcsafe, raises: [].}): bool =
|
2021-10-25 07:58:13 +00:00
|
|
|
let maybeElem = buff.get(i)
|
|
|
|
if (maybeElem.isSome()):
|
|
|
|
let elem = maybeElem.unsafeGet()
|
2022-11-09 17:57:04 +00:00
|
|
|
check(elem)
|
2021-10-25 07:58:13 +00:00
|
|
|
else:
|
|
|
|
false
|
|
|
|
|
2022-03-24 14:56:00 +00:00
|
|
|
proc `[]`*[A](buff: var GrowableCircularBuffer[A], i: uint32): var A =
|
2023-05-10 13:50:04 +00:00
|
|
|
## Returns contents of the `var GrowableCircularBuffer`. If it is not set,
|
|
|
|
## then an exception is thrown.
|
2021-10-25 07:58:13 +00:00
|
|
|
buff.items[i and buff.mask].get()
|
|
|
|
|
2021-10-15 11:38:51 +00:00
|
|
|
proc len*[A](buff: GrowableCircularBuffer[A]): int =
|
2022-03-24 14:56:00 +00:00
|
|
|
int(buff.mask) + 1
|
|
|
|
|
|
|
|
proc calculateNextMask(currentMask: uint32, index:uint32): uint32 =
|
|
|
|
# Increase mask,so that index will fit in buffer size i.e mask + 1
|
|
|
|
if currentMask == uint32.high:
|
|
|
|
return currentMask
|
2022-07-09 08:55:15 +00:00
|
|
|
|
|
|
|
var newSize = currentMask + 1
|
2022-03-24 14:56:00 +00:00
|
|
|
while true:
|
|
|
|
newSize = newSize * 2
|
|
|
|
if newSize == 0 or index < newSize:
|
|
|
|
break
|
|
|
|
return newSize - 1
|
2021-10-15 11:38:51 +00:00
|
|
|
|
|
|
|
# Item contains the element we want to make space for
|
|
|
|
# index is the index in the list.
|
2023-05-10 13:50:04 +00:00
|
|
|
proc ensureSize*[A](
|
|
|
|
buff: var GrowableCircularBuffer[A], item: uint32, index: uint32) =
|
2021-10-15 11:38:51 +00:00
|
|
|
if (index > buff.mask):
|
2022-03-24 14:56:00 +00:00
|
|
|
let newMask = calculateNextMask(buff.mask, index)
|
|
|
|
var newSeq = newSeq[Option[A]](int(newMask) + 1)
|
|
|
|
var i = 0'u32
|
2021-10-15 11:38:51 +00:00
|
|
|
while i <= buff.mask:
|
|
|
|
let idx = item - index + i
|
|
|
|
newSeq[idx and newMask] = buff.get(idx)
|
|
|
|
inc i
|
|
|
|
buff.items = move(newSeq)
|
|
|
|
buff.mask = newMask
|
|
|
|
|
|
|
|
iterator items*[A](buff: GrowableCircularBuffer[A]): Option[A] =
|
|
|
|
for e in buff.items:
|
|
|
|
yield e
|