2022-07-01 18:19:57 +00:00
|
|
|
# Nim-Libp2p
|
2023-01-20 14:47:40 +00:00
|
|
|
# Copyright (c) 2023 Status Research & Development GmbH
|
2022-07-01 18:19:57 +00:00
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
|
|
# at your option.
|
|
|
|
# This file may not be copied, modified, or distributed except according to
|
|
|
|
# those terms.
|
2018-11-27 12:16:04 +00:00
|
|
|
|
|
|
|
## This module implements variable buffer.
|
2020-05-31 14:22:49 +00:00
|
|
|
|
2023-06-07 11:12:49 +00:00
|
|
|
{.push raises: [].}
|
2020-05-31 14:22:49 +00:00
|
|
|
|
2018-11-27 12:16:04 +00:00
|
|
|
import varint, strutils
|
|
|
|
|
|
|
|
type VBuffer* = object
|
|
|
|
buffer*: seq[byte]
|
|
|
|
offset*: int
|
|
|
|
|
|
|
|
template isEmpty*(vb: VBuffer): bool =
|
|
|
|
## Returns ``true`` if buffer ``vb`` is empty.
|
|
|
|
len(vb.buffer) - vb.offset <= 0
|
|
|
|
|
|
|
|
template isEnough*(vb: VBuffer, length: int): bool =
|
|
|
|
## Returns ``true`` if buffer ``vb`` holds at least ``length`` bytes.
|
|
|
|
len(vb.buffer) - vb.offset - length >= 0
|
|
|
|
|
2020-05-18 05:25:55 +00:00
|
|
|
proc high*(vb: VBuffer): int =
|
|
|
|
## Returns number of bytes left in buffer ``vb``.
|
|
|
|
result = vb.buffer.high - vb.offset
|
|
|
|
doAssert(result >= 0)
|
|
|
|
|
2018-11-27 12:16:04 +00:00
|
|
|
proc len*(vb: VBuffer): int =
|
|
|
|
## Returns number of bytes left in buffer ``vb``.
|
|
|
|
result = len(vb.buffer) - vb.offset
|
2020-05-18 05:25:55 +00:00
|
|
|
doAssert(result >= 0)
|
2018-11-27 12:16:04 +00:00
|
|
|
|
|
|
|
proc initVBuffer*(data: seq[byte], offset = 0): VBuffer =
|
|
|
|
## Initialize VBuffer with shallow copy of ``data``.
|
2022-10-20 12:52:02 +00:00
|
|
|
result.buffer = data
|
2018-11-27 12:16:04 +00:00
|
|
|
result.offset = offset
|
|
|
|
|
2021-12-16 10:05:20 +00:00
|
|
|
proc initVBuffer*(data: openArray[byte], offset = 0): VBuffer =
|
2018-11-27 12:16:04 +00:00
|
|
|
## Initialize VBuffer with copy of ``data``.
|
2023-07-11 10:17:28 +00:00
|
|
|
result.buffer = newSeqUninitialized[byte](len(data))
|
2018-11-27 12:16:04 +00:00
|
|
|
if len(data) > 0:
|
|
|
|
copyMem(addr result.buffer[0], unsafeAddr data[0], len(data))
|
|
|
|
result.offset = offset
|
|
|
|
|
|
|
|
proc initVBuffer*(): VBuffer =
|
|
|
|
## Initialize empty VBuffer.
|
|
|
|
result.buffer = newSeqOfCap[byte](128)
|
|
|
|
|
2019-12-04 04:44:54 +00:00
|
|
|
proc writePBVarint*(vb: var VBuffer, value: PBSomeUVarint) =
|
|
|
|
## Write ``value`` as variable unsigned integer.
|
|
|
|
var length = 0
|
|
|
|
var v = value and cast[type(value)](0xFFFF_FFFF_FFFF_FFFF)
|
|
|
|
vb.buffer.setLen(len(vb.buffer) + vsizeof(v))
|
2020-05-18 05:25:55 +00:00
|
|
|
let res = PB.putUVarint(toOpenArray(vb.buffer, vb.offset, vb.buffer.high), length, v)
|
2020-05-31 14:22:49 +00:00
|
|
|
doAssert(res.isOk())
|
2019-12-04 04:44:54 +00:00
|
|
|
vb.offset += length
|
|
|
|
|
|
|
|
proc writeLPVarint*(vb: var VBuffer, value: LPSomeUVarint) =
|
2018-11-27 12:16:04 +00:00
|
|
|
## Write ``value`` as variable unsigned integer.
|
|
|
|
var length = 0
|
2019-03-15 22:47:04 +00:00
|
|
|
# LibP2P varint supports only 63 bits.
|
|
|
|
var v = value and cast[type(value)](0x7FFF_FFFF_FFFF_FFFF)
|
2018-11-27 12:16:04 +00:00
|
|
|
vb.buffer.setLen(len(vb.buffer) + vsizeof(v))
|
2020-05-18 05:25:55 +00:00
|
|
|
let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, vb.buffer.high), length, v)
|
2020-05-31 14:22:49 +00:00
|
|
|
doAssert(res.isOk())
|
2018-11-27 12:16:04 +00:00
|
|
|
vb.offset += length
|
|
|
|
|
2020-03-06 19:19:43 +00:00
|
|
|
proc writeVarint*(vb: var VBuffer, value: LPSomeUVarint) =
|
2019-12-04 04:44:54 +00:00
|
|
|
writeLPVarint(vb, value)
|
|
|
|
|
2021-12-16 10:05:20 +00:00
|
|
|
proc writeSeq*[T: byte | char](vb: var VBuffer, value: openArray[T]) =
|
2018-11-27 12:16:04 +00:00
|
|
|
## Write array ``value`` to buffer ``vb``, value will be prefixed with
|
|
|
|
## varint length of the array.
|
|
|
|
var length = 0
|
2020-03-06 19:19:43 +00:00
|
|
|
vb.buffer.setLen(len(vb.buffer) + vsizeof(uint(len(value))) + len(value))
|
2020-05-18 05:25:55 +00:00
|
|
|
let res = LP.putUVarint(
|
|
|
|
toOpenArray(vb.buffer, vb.offset, vb.buffer.high), length, uint(len(value))
|
2018-11-27 12:16:04 +00:00
|
|
|
)
|
2020-05-31 14:22:49 +00:00
|
|
|
doAssert(res.isOk())
|
2018-11-27 12:16:04 +00:00
|
|
|
vb.offset += length
|
|
|
|
if len(value) > 0:
|
|
|
|
copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
|
|
|
|
vb.offset += len(value)
|
|
|
|
|
2021-12-16 10:05:20 +00:00
|
|
|
proc writeArray*[T: byte | char](vb: var VBuffer, value: openArray[T]) =
|
2018-11-27 12:16:04 +00:00
|
|
|
## Write array ``value`` to buffer ``vb``, value will NOT be prefixed with
|
|
|
|
## varint length of the array.
|
|
|
|
if len(value) > 0:
|
|
|
|
vb.buffer.setLen(len(vb.buffer) + len(value))
|
|
|
|
copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
|
|
|
|
vb.offset += len(value)
|
|
|
|
|
|
|
|
proc finish*(vb: var VBuffer) =
|
|
|
|
## Finishes ``vb``.
|
|
|
|
vb.offset = 0
|
|
|
|
|
|
|
|
proc peekVarint*(vb: var VBuffer, value: var LPSomeUVarint): int =
|
|
|
|
## Peek unsigned integer from buffer ``vb`` and store result to ``value``.
|
|
|
|
##
|
|
|
|
## This procedure will not adjust internal offset.
|
|
|
|
##
|
|
|
|
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
|
|
|
|
result = -1
|
|
|
|
value = cast[type(value)](0)
|
|
|
|
var length = 0
|
|
|
|
if not vb.isEmpty():
|
|
|
|
let res =
|
2020-05-18 05:25:55 +00:00
|
|
|
LP.getUVarint(toOpenArray(vb.buffer, vb.offset, vb.buffer.high), length, value)
|
2020-05-31 14:22:49 +00:00
|
|
|
if res.isOk():
|
2018-11-27 12:16:04 +00:00
|
|
|
result = length
|
|
|
|
|
|
|
|
proc peekSeq*[T: string | seq[byte]](vb: var VBuffer, value: var T): int =
|
|
|
|
## Peek length prefixed array from buffer ``vb`` and store result to
|
|
|
|
## ``value``.
|
|
|
|
##
|
|
|
|
## This procedure will not adjust internal offset.
|
|
|
|
##
|
|
|
|
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
|
|
|
|
result = -1
|
|
|
|
value.setLen(0)
|
|
|
|
var length = 0
|
|
|
|
var size = 0'u64
|
|
|
|
if not vb.isEmpty() and
|
2020-05-31 14:22:49 +00:00
|
|
|
LP
|
|
|
|
.getUVarint(toOpenArray(vb.buffer, vb.offset, vb.buffer.high), length, size)
|
|
|
|
.isOk():
|
2018-11-27 12:16:04 +00:00
|
|
|
vb.offset += length
|
|
|
|
result = length
|
|
|
|
if vb.isEnough(int(size)):
|
|
|
|
value.setLen(size)
|
|
|
|
if size > 0'u64:
|
|
|
|
copyMem(addr value[0], addr vb.buffer[vb.offset], size)
|
|
|
|
result += int(size)
|
|
|
|
vb.offset -= length
|
|
|
|
|
|
|
|
proc peekArray*[T: char | byte](vb: var VBuffer, value: var openArray[T]): int =
|
|
|
|
## Peek array from buffer ``vb`` and store result to ``value``.
|
|
|
|
##
|
|
|
|
## This procedure will not adjust internal offset.
|
|
|
|
##
|
|
|
|
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
|
|
|
|
result = -1
|
|
|
|
let length = len(value)
|
|
|
|
if vb.isEnough(length):
|
|
|
|
if length > 0:
|
|
|
|
copyMem(addr value[0], addr vb.buffer[vb.offset], length)
|
|
|
|
result = length
|
|
|
|
|
|
|
|
proc readVarint*(vb: var VBuffer, value: var LPSomeUVarint): int {.inline.} =
|
|
|
|
## Read unsigned integer from buffer ``vb`` and store result to ``value``.
|
|
|
|
##
|
|
|
|
## Returns number of bytes consumed from ``vb`` or ``-1`` on error.
|
|
|
|
result = vb.peekVarint(value)
|
|
|
|
if result != -1:
|
|
|
|
vb.offset += result
|
|
|
|
|
|
|
|
proc readSeq*[T: string | seq[byte]](vb: var VBuffer, value: var T): int {.inline.} =
|
|
|
|
## Read length prefixed array from buffer ``vb`` and store result to
|
|
|
|
## ``value``.
|
|
|
|
##
|
|
|
|
## Returns number of bytes consumed from ``vb`` or ``-1`` on error.
|
|
|
|
result = vb.peekSeq(value)
|
|
|
|
if result != -1:
|
|
|
|
vb.offset += result
|
|
|
|
|
|
|
|
proc readArray*[T: char | byte](
|
2021-12-16 10:05:20 +00:00
|
|
|
vb: var VBuffer, value: var openArray[T]
|
|
|
|
): int {.inline.} =
|
2018-11-27 12:16:04 +00:00
|
|
|
## Read array from buffer ``vb`` and store result to ``value``.
|
|
|
|
##
|
|
|
|
## Returns number of bytes consumed from ``vb`` or ``-1`` on error.
|
|
|
|
result = vb.peekArray(value)
|
|
|
|
if result != -1:
|
|
|
|
vb.offset += result
|
|
|
|
|
|
|
|
proc `$`*(vb: VBuffer): string =
|
|
|
|
## Return hexadecimal string representation of buffer ``vb``.
|
|
|
|
let length = (len(vb.buffer) - vb.offset) * 2
|
|
|
|
result = newStringOfCap(length)
|
|
|
|
for i in 0 ..< len(vb.buffer):
|
|
|
|
result.add(toHex(vb.buffer[i]))
|