parent
d45f35d295
commit
3b554d0f51
|
@ -11,5 +11,5 @@ requires "nim > 0.18.0",
|
||||||
"https://github.com/status-im/nim-asyncdispatch2"
|
"https://github.com/status-im/nim-asyncdispatch2"
|
||||||
|
|
||||||
task test, "Runs the test suite":
|
task test, "Runs the test suite":
|
||||||
exec "nim c -r tests/testpbvarint"
|
exec "nim c -r tests/testvarint"
|
||||||
exec "nim c -r tests/testdaemon"
|
exec "nim c -r tests/testdaemon"
|
|
@ -10,7 +10,7 @@
|
||||||
## This module implementes API for `go-libp2p-daemon`.
|
## This module implementes API for `go-libp2p-daemon`.
|
||||||
import os, osproc, strutils, tables, streams
|
import os, osproc, strutils, tables, streams
|
||||||
import asyncdispatch2
|
import asyncdispatch2
|
||||||
import ../protobuf/varint, ../protobuf/minprotobuf, transpool
|
import ../varint, ../protobuf/minprotobuf, transpool
|
||||||
|
|
||||||
when not defined(windows):
|
when not defined(windows):
|
||||||
import posix
|
import posix
|
||||||
|
@ -367,7 +367,7 @@ proc recvMessage(conn: StreamTransport): Future[seq[byte]] {.async.} =
|
||||||
var buffer = newSeq[byte](10)
|
var buffer = newSeq[byte](10)
|
||||||
for i in 0..<len(buffer):
|
for i in 0..<len(buffer):
|
||||||
await conn.readExactly(addr buffer[i], 1)
|
await conn.readExactly(addr buffer[i], 1)
|
||||||
res = getUVarint(buffer.toOpenArray(0, i), length, size)
|
res = PB.getUVarint(buffer.toOpenArray(0, i), length, size)
|
||||||
if res == VarintStatus.Success:
|
if res == VarintStatus.Success:
|
||||||
break
|
break
|
||||||
if res != VarintStatus.Success or size > MaxMessageSize:
|
if res != VarintStatus.Success or size > MaxMessageSize:
|
||||||
|
@ -852,15 +852,3 @@ proc dhtSearchValue*(api: DaemonAPI, key: string,
|
||||||
result = list
|
result = list
|
||||||
finally:
|
finally:
|
||||||
api.pool.release(transp)
|
api.pool.release(transp)
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
proc test() {.async.} =
|
|
||||||
var api1 = await newDaemonApi(sockpath = "/tmp/p2pd-1.sock")
|
|
||||||
var api2 = await newDaemonApi(sockpath = "/tmp/p2pd-2.sock")
|
|
||||||
echo await api1.identity()
|
|
||||||
echo await api2.identity()
|
|
||||||
await sleepAsync(1000)
|
|
||||||
await api1.close()
|
|
||||||
await api2.close()
|
|
||||||
|
|
||||||
waitFor test()
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
## This module implements minimal Google's ProtoBuf primitives.
|
## This module implements minimal Google's ProtoBuf primitives.
|
||||||
import varint
|
import ../varint
|
||||||
|
|
||||||
const
|
const
|
||||||
MaxMessageSize* = 1'u shl 22
|
MaxMessageSize* = 1'u shl 22
|
||||||
|
@ -134,12 +134,12 @@ proc write*(pb: var ProtoBuffer, field: ProtoField) =
|
||||||
var length = 0
|
var length = 0
|
||||||
var res: VarintStatus
|
var res: VarintStatus
|
||||||
pb.buffer.setLen(len(pb.buffer) + vsizeof(field))
|
pb.buffer.setLen(len(pb.buffer) + vsizeof(field))
|
||||||
res = putUVarint(pb.toOpenArray(), length, protoHeader(field))
|
res = PB.putUVarint(pb.toOpenArray(), length, protoHeader(field))
|
||||||
assert(res == VarintStatus.Success)
|
assert(res == VarintStatus.Success)
|
||||||
pb.offset += length
|
pb.offset += length
|
||||||
case field.kind
|
case field.kind
|
||||||
of ProtoFieldKind.Varint:
|
of ProtoFieldKind.Varint:
|
||||||
res = putUVarint(pb.toOpenArray(), length, field.vint)
|
res = PB.putUVarint(pb.toOpenArray(), length, field.vint)
|
||||||
assert(res == VarintStatus.Success)
|
assert(res == VarintStatus.Success)
|
||||||
pb.offset += length
|
pb.offset += length
|
||||||
of ProtoFieldKind.Fixed64:
|
of ProtoFieldKind.Fixed64:
|
||||||
|
@ -163,7 +163,7 @@ proc write*(pb: var ProtoBuffer, field: ProtoField) =
|
||||||
pb.buffer[pb.offset + 3] = byte((value shr 24) and 0xFF'u32)
|
pb.buffer[pb.offset + 3] = byte((value shr 24) and 0xFF'u32)
|
||||||
pb.offset += 4
|
pb.offset += 4
|
||||||
of ProtoFieldKind.Length:
|
of ProtoFieldKind.Length:
|
||||||
res = putUVarint(pb.toOpenArray(), length, uint(len(field.vbuffer)))
|
res = PB.putUVarint(pb.toOpenArray(), length, uint(len(field.vbuffer)))
|
||||||
assert(res == VarintStatus.Success)
|
assert(res == VarintStatus.Success)
|
||||||
pb.offset += length
|
pb.offset += length
|
||||||
assert(pb.isEnough(len(field.vbuffer)))
|
assert(pb.isEnough(len(field.vbuffer)))
|
||||||
|
@ -180,7 +180,7 @@ proc finish*(pb: var ProtoBuffer) =
|
||||||
if WithVarintLength in pb.options:
|
if WithVarintLength in pb.options:
|
||||||
let size = uint(len(pb.buffer) - 10)
|
let size = uint(len(pb.buffer) - 10)
|
||||||
let pos = 10 - vsizeof(length)
|
let pos = 10 - vsizeof(length)
|
||||||
let res = putUVarint(pb.buffer.toOpenArray(pos, 9), length, size)
|
let res = PB.putUVarint(pb.buffer.toOpenArray(pos, 9), length, size)
|
||||||
assert(res == VarintStatus.Success)
|
assert(res == VarintStatus.Success)
|
||||||
pb.offset = pos
|
pb.offset = pos
|
||||||
else:
|
else:
|
||||||
|
@ -194,14 +194,14 @@ proc getVarintValue*(data: var ProtoBuffer, field: int,
|
||||||
var soffset = data.offset
|
var soffset = data.offset
|
||||||
|
|
||||||
if not data.isEmpty() and
|
if not data.isEmpty() and
|
||||||
getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success:
|
PB.getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success:
|
||||||
data.offset += length
|
data.offset += length
|
||||||
if header == protoHeader(field, Varint):
|
if header == protoHeader(field, Varint):
|
||||||
if not data.isEmpty():
|
if not data.isEmpty():
|
||||||
when type(value) is int32 or type(value) is int64 or type(value) is int:
|
when type(value) is int32 or type(value) is int64 or type(value) is int:
|
||||||
let res = getSVarint(data.toOpenArray(), length, value)
|
let res = getSVarint(data.toOpenArray(), length, value)
|
||||||
else:
|
else:
|
||||||
let res = getUVarint(data.toOpenArray(), length, value)
|
let res = PB.getUVarint(data.toOpenArray(), length, value)
|
||||||
data.offset += length
|
data.offset += length
|
||||||
result = length
|
result = length
|
||||||
return
|
return
|
||||||
|
@ -218,11 +218,11 @@ proc getLengthValue*[T: string|seq[byte]](data: var ProtoBuffer, field: int,
|
||||||
result = -1
|
result = -1
|
||||||
buffer.setLen(0)
|
buffer.setLen(0)
|
||||||
if not data.isEmpty() and
|
if not data.isEmpty() and
|
||||||
getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success:
|
PB.getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success:
|
||||||
data.offset += length
|
data.offset += length
|
||||||
if header == protoHeader(field, Length):
|
if header == protoHeader(field, Length):
|
||||||
if not data.isEmpty() and
|
if not data.isEmpty() and
|
||||||
getUVarint(data.toOpenArray(), length, ssize) == VarintStatus.Success:
|
PB.getUVarint(data.toOpenArray(), length, ssize) == VarintStatus.Success:
|
||||||
data.offset += length
|
data.offset += length
|
||||||
if ssize <= MaxMessageSize and data.isEnough(int(ssize)):
|
if ssize <= MaxMessageSize and data.isEnough(int(ssize)):
|
||||||
buffer.setLen(ssize)
|
buffer.setLen(ssize)
|
||||||
|
@ -255,11 +255,11 @@ proc enterSubmessage*(pb: var ProtoBuffer): int =
|
||||||
var soffset = pb.offset
|
var soffset = pb.offset
|
||||||
|
|
||||||
if not pb.isEmpty() and
|
if not pb.isEmpty() and
|
||||||
getUVarint(pb.toOpenArray(), length, header) == VarintStatus.Success:
|
PB.getUVarint(pb.toOpenArray(), length, header) == VarintStatus.Success:
|
||||||
pb.offset += length
|
pb.offset += length
|
||||||
if (header and 0x07'u64) == cast[uint64](ProtoFieldKind.Length):
|
if (header and 0x07'u64) == cast[uint64](ProtoFieldKind.Length):
|
||||||
if not pb.isEmpty() and
|
if not pb.isEmpty() and
|
||||||
getUVarint(pb.toOpenArray(), length, msize) == VarintStatus.Success:
|
PB.getUVarint(pb.toOpenArray(), length, msize) == VarintStatus.Success:
|
||||||
pb.offset += length
|
pb.offset += length
|
||||||
if msize <= MaxMessageSize and pb.isEnough(int(msize)):
|
if msize <= MaxMessageSize and pb.isEnough(int(msize)):
|
||||||
pb.length = int(msize)
|
pb.length = int(msize)
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
## This file may not be copied, modified, or distributed except according to
|
## This file may not be copied, modified, or distributed except according to
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
## This module implements Google ProtoBuf's variable integer `VARINT`.
|
## This module implements Variable Integer `VARINT`.
|
||||||
|
## This module supports two variants of variable integer
|
||||||
|
## - Google ProtoBuf varint, which is able to encode full uint64 number and
|
||||||
|
## maximum size of encoded value is 10 octets (bytes).
|
||||||
|
## - LibP2P varint, which is able to encode only 63bits of uint64 number and
|
||||||
|
## maximum size of encoded value is 9 octets (bytes).
|
||||||
import bitops
|
import bitops
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -18,19 +23,30 @@ type
|
||||||
Incomplete,
|
Incomplete,
|
||||||
Overrun
|
Overrun
|
||||||
|
|
||||||
SomeUVarint* = uint | uint64 | uint32
|
PB* = object
|
||||||
SomeSVarint* = int | int64 | int32
|
## Use this type to specify Google ProtoBuf's varint encoding
|
||||||
SomeVarint* = SomeUVarint | SomeSVarint
|
LP* = object
|
||||||
|
## Use this type to specify LibP2P varint encoding
|
||||||
|
|
||||||
|
PBSomeUVarint* = uint | uint64 | uint32
|
||||||
|
PBSomeSVarint* = int | int64 | int32
|
||||||
|
PBSomeVarint* = PBSomeUVarint | PBSomeSVarint
|
||||||
|
LPSomeUVarint* = uint | uint64 | uint32 | uint16 | uint8
|
||||||
|
LPSomeVarint* = LPSomeUVarint
|
||||||
|
SomeVarint* = PBSomeVarint | LPSomeVarint
|
||||||
|
SomeUVarint* = PBSomeUVarint | LPSomeUVarint
|
||||||
VarintError* = object of Exception
|
VarintError* = object of Exception
|
||||||
|
|
||||||
proc vsizeof*(x: SomeUVarint|SomeSVarint): int {.inline.} =
|
proc vsizeof*(x: SomeVarint): int {.inline.} =
|
||||||
## Returns number of bytes required to encode integer ``x`` as varint.
|
## Returns number of bytes required to encode integer ``x`` as varint.
|
||||||
if x == cast[type(x)](0):
|
if x == cast[type(x)](0):
|
||||||
result = 1
|
result = 1
|
||||||
else:
|
else:
|
||||||
result = (fastLog2(x) + 1 + 7 - 1) div 7
|
result = (fastLog2(x) + 1 + 7 - 1) div 7
|
||||||
|
|
||||||
proc getUVarint*(pbytes: openarray[byte], outlen: var int,
|
proc getUVarint*[T: PB|LP](vtype: typedesc[T],
|
||||||
|
pbytes: openarray[byte],
|
||||||
|
outlen: var int,
|
||||||
outval: var SomeUVarint): VarintStatus =
|
outval: var SomeUVarint): VarintStatus =
|
||||||
## Decode `unsigned varint` from buffer ``pbytes`` and store it to ``outval``.
|
## Decode `unsigned varint` from buffer ``pbytes`` and store it to ``outval``.
|
||||||
## On success ``outlen`` will be set to number of bytes processed while
|
## On success ``outlen`` will be set to number of bytes processed while
|
||||||
|
@ -44,11 +60,23 @@ proc getUVarint*(pbytes: openarray[byte], outlen: var int,
|
||||||
## If encoded value can produce integer overflow, ``Overflow`` error will be
|
## If encoded value can produce integer overflow, ``Overflow`` error will be
|
||||||
## returned.
|
## returned.
|
||||||
##
|
##
|
||||||
## Note, when decoding 10th byte of 64bit integer only 1 bit from byte will be
|
## Google ProtoBuf
|
||||||
## decoded, all other bits will be ignored. When decoding 5th byte of 32bit
|
## When decoding 10th byte of Google Protobuf's 64bit integer only 1 bit from
|
||||||
## integer only 4 bits from byte will be decoded, all other bits will be
|
## byte will be decoded, all other bits will be ignored. When decoding 5th
|
||||||
## ignored.
|
## byte of 32bit integer only 4 bits from byte will be decoded, all other bits
|
||||||
|
## will be ignored.
|
||||||
|
##
|
||||||
|
## LibP2P
|
||||||
|
## When decoding 5th byte of 32bit integer only 4 bits from byte will be
|
||||||
|
## decoded, all other bits will be ignored.
|
||||||
|
when vtype is PB:
|
||||||
const MaxBits = byte(sizeof(outval) * 8)
|
const MaxBits = byte(sizeof(outval) * 8)
|
||||||
|
else:
|
||||||
|
when sizeof(outval) == 8:
|
||||||
|
const MaxBits = 63'u8
|
||||||
|
else:
|
||||||
|
const MaxBits = byte(sizeof(outval) * 8)
|
||||||
|
|
||||||
var shift = 0'u8
|
var shift = 0'u8
|
||||||
result = VarintStatus.Incomplete
|
result = VarintStatus.Incomplete
|
||||||
outlen = 0
|
outlen = 0
|
||||||
|
@ -67,12 +95,13 @@ proc getUVarint*(pbytes: openarray[byte], outlen: var int,
|
||||||
if (b and 0x80'u8) == 0'u8:
|
if (b and 0x80'u8) == 0'u8:
|
||||||
result = VarintStatus.Success
|
result = VarintStatus.Success
|
||||||
break
|
break
|
||||||
|
|
||||||
if result == VarintStatus.Incomplete:
|
if result == VarintStatus.Incomplete:
|
||||||
outlen = 0
|
outlen = 0
|
||||||
outval = cast[type(outval)](0)
|
outval = cast[type(outval)](0)
|
||||||
|
|
||||||
proc putUVarint*(pbytes: var openarray[byte], outlen: var int,
|
proc putUVarint*[T: PB|LP](vtype: typedesc[T],
|
||||||
|
pbytes: var openarray[byte],
|
||||||
|
outlen: var int,
|
||||||
outval: SomeUVarint): VarintStatus =
|
outval: SomeUVarint): VarintStatus =
|
||||||
## Encode `unsigned varint` ``outval`` and store it to array ``pbytes``.
|
## Encode `unsigned varint` ``outval`` and store it to array ``pbytes``.
|
||||||
##
|
##
|
||||||
|
@ -83,12 +112,23 @@ proc putUVarint*(pbytes: var openarray[byte], outlen: var int,
|
||||||
## error will be returned and ``outlen`` will be set to number of bytes
|
## error will be returned and ``outlen`` will be set to number of bytes
|
||||||
## required.
|
## required.
|
||||||
##
|
##
|
||||||
|
## Google ProtoBuf
|
||||||
## Maximum encoded length of 64bit integer is 10 octets.
|
## Maximum encoded length of 64bit integer is 10 octets.
|
||||||
## Maximum encoded length of 32bit integer is 5 octets.
|
## Maximum encoded length of 32bit integer is 5 octets.
|
||||||
|
##
|
||||||
|
## LibP2P
|
||||||
|
## Maximum encoded length of 63bit integer is 9 octets.
|
||||||
|
## Maximum encoded length of 32bit integer is 5 octets.
|
||||||
var buffer: array[10, byte]
|
var buffer: array[10, byte]
|
||||||
var value = outval
|
var value = outval
|
||||||
var k = 0
|
var k = 0
|
||||||
|
|
||||||
|
when vtype is LP:
|
||||||
|
if sizeof(outval) == 8:
|
||||||
|
if (cast[uint64](outval) and 0x8000_0000_0000_0000'u64) != 0'u64:
|
||||||
|
result = Overflow
|
||||||
|
return
|
||||||
|
|
||||||
if value <= cast[type(outval)](0x7F):
|
if value <= cast[type(outval)](0x7F):
|
||||||
buffer[0] = cast[byte](outval and 0xFF)
|
buffer[0] = cast[byte](outval and 0xFF)
|
||||||
inc(k)
|
inc(k)
|
||||||
|
@ -107,10 +147,10 @@ proc putUVarint*(pbytes: var openarray[byte], outlen: var int,
|
||||||
result = VarintStatus.Overrun
|
result = VarintStatus.Overrun
|
||||||
|
|
||||||
proc getSVarint*(pbytes: openarray[byte], outsize: var int,
|
proc getSVarint*(pbytes: openarray[byte], outsize: var int,
|
||||||
outval: var SomeSVarint): VarintStatus {.inline.} =
|
outval: var PBSomeSVarint): VarintStatus {.inline.} =
|
||||||
## Decode `signed varint` from buffer ``pbytes`` and store it to ``outval``.
|
## Decode Google ProtoBuf's `signed varint` from buffer ``pbytes`` and store
|
||||||
## On success ``outlen`` will be set to number of bytes processed while
|
## it to ``outval``. On success ``outlen`` will be set to number of bytes
|
||||||
## decoding `signed varint`.
|
## processed while decoding `signed varint`.
|
||||||
##
|
##
|
||||||
## If array ``pbytes`` is empty, ``Incomplete`` error will be returned.
|
## If array ``pbytes`` is empty, ``Incomplete`` error will be returned.
|
||||||
##
|
##
|
||||||
|
@ -129,7 +169,7 @@ proc getSVarint*(pbytes: openarray[byte], outsize: var int,
|
||||||
else:
|
else:
|
||||||
var value: uint32
|
var value: uint32
|
||||||
|
|
||||||
result = getUVarint(pbytes, outsize, value)
|
result = PB.getUVarint(pbytes, outsize, value)
|
||||||
if result == VarintStatus.Success:
|
if result == VarintStatus.Success:
|
||||||
if (value and cast[type(value)](1)) != cast[type(value)](0):
|
if (value and cast[type(value)](1)) != cast[type(value)](0):
|
||||||
outval = cast[type(outval)](not(value shr 1))
|
outval = cast[type(outval)](not(value shr 1))
|
||||||
|
@ -137,8 +177,9 @@ proc getSVarint*(pbytes: openarray[byte], outsize: var int,
|
||||||
outval = cast[type(outval)](value shr 1)
|
outval = cast[type(outval)](value shr 1)
|
||||||
|
|
||||||
proc putSVarint*(pbytes: var openarray[byte], outsize: var int,
|
proc putSVarint*(pbytes: var openarray[byte], outsize: var int,
|
||||||
outval: SomeSVarint): VarintStatus {.inline.} =
|
outval: PBSomeSVarint): VarintStatus {.inline.} =
|
||||||
## Encode `signed varint` ``outval`` and store it to array ``pbytes``.
|
## Encode Google ProtoBuf's `signed varint` ``outval`` and store it to array
|
||||||
|
## ``pbytes``.
|
||||||
##
|
##
|
||||||
## On success ``outlen`` will hold number of bytes (octets) used to encode
|
## On success ``outlen`` will hold number of bytes (octets) used to encode
|
||||||
## unsigned integer ``v``.
|
## unsigned integer ``v``.
|
||||||
|
@ -161,11 +202,12 @@ proc putSVarint*(pbytes: var openarray[byte], outsize: var int,
|
||||||
not(cast[uint32](outval) shl 1)
|
not(cast[uint32](outval) shl 1)
|
||||||
else:
|
else:
|
||||||
cast[uint32](outval) shl 1
|
cast[uint32](outval) shl 1
|
||||||
result = putUVarint(pbytes, outsize, value)
|
result = PB.putUVarint(pbytes, outsize, value)
|
||||||
|
|
||||||
proc encodeVarint*(value: SomeUVarint|SomeSVarint): seq[byte] {.inline.} =
|
proc encodeVarint*(vtype: typedesc[PB],
|
||||||
## Encode integer to `signed/unsigned varint` and returns sequence of bytes
|
value: PBSomeVarint): seq[byte] {.inline.} =
|
||||||
## as result.
|
## Encode integer to Google ProtoBuf's `signed/unsigned varint` and returns
|
||||||
|
## sequence of bytes as result.
|
||||||
var outsize = 0
|
var outsize = 0
|
||||||
result = newSeqOfCap[byte](10)
|
result = newSeqOfCap[byte](10)
|
||||||
when sizeof(value) == 4:
|
when sizeof(value) == 4:
|
||||||
|
@ -181,6 +223,26 @@ proc encodeVarint*(value: SomeUVarint|SomeSVarint): seq[byte] {.inline.} =
|
||||||
else:
|
else:
|
||||||
raise newException(VarintError, "Error '" & $res & "'")
|
raise newException(VarintError, "Error '" & $res & "'")
|
||||||
|
|
||||||
|
proc encodeVarint*(vtype: typedesc[LP],
|
||||||
|
value: LPSomeVarint): seq[byte] {.inline.} =
|
||||||
|
## Encode integer to LibP2P `unsigned varint` and returns sequence of bytes
|
||||||
|
## as result.
|
||||||
|
var outsize = 0
|
||||||
|
result = newSeqOfCap[byte](9)
|
||||||
|
when sizeof(value) == 1:
|
||||||
|
result.setLen(2)
|
||||||
|
elif sizeof(value) == 2:
|
||||||
|
result.setLen(3)
|
||||||
|
elif sizeof(value) == 4:
|
||||||
|
result.setLen(5)
|
||||||
|
else:
|
||||||
|
result.setLen(9)
|
||||||
|
let res = LP.putUVarint(result, outsize, value)
|
||||||
|
if res == VarintStatus.Success:
|
||||||
|
result.setLen(outsize)
|
||||||
|
else:
|
||||||
|
raise newException(VarintError, "Error '" & $res & "'")
|
||||||
|
|
||||||
proc decodeSVarint*(data: openarray[byte]): int {.inline.} =
|
proc decodeSVarint*(data: openarray[byte]): int {.inline.} =
|
||||||
## Decode signed integer from array ``data`` and return it as result.
|
## Decode signed integer from array ``data`` and return it as result.
|
||||||
var outsize = 0
|
var outsize = 0
|
||||||
|
@ -188,91 +250,10 @@ proc decodeSVarint*(data: openarray[byte]): int {.inline.} =
|
||||||
if res != VarintStatus.Success:
|
if res != VarintStatus.Success:
|
||||||
raise newException(VarintError, "Error '" & $res & "'")
|
raise newException(VarintError, "Error '" & $res & "'")
|
||||||
|
|
||||||
proc decodeUVarint*(data: openarray[byte]): uint {.inline.} =
|
proc decodeUVarint*[T: PB|LP](vtype: typedesc[T],
|
||||||
|
data: openarray[byte]): uint {.inline.} =
|
||||||
## Decode unsigned integer from array ``data`` and return it as result.
|
## Decode unsigned integer from array ``data`` and return it as result.
|
||||||
var outsize = 0
|
var outsize = 0
|
||||||
let res = getUVarint(data, outsize, result)
|
let res = vtype.getUVarint(data, outsize, result)
|
||||||
if res != VarintStatus.Success:
|
if res != VarintStatus.Success:
|
||||||
raise newException(VarintError, "Error '" & $res & "'")
|
raise newException(VarintError, "Error '" & $res & "'")
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
const edgeValues = [
|
|
||||||
0'u64, (1'u64 shl 7) - 1'u64,
|
|
||||||
(1'u64 shl 7), (1'u64 shl 14) - 1'u64,
|
|
||||||
(1'u64 shl 14), (1'u64 shl 21) - 1'u64,
|
|
||||||
(1'u64 shl 21), (1'u64 shl 28) - 1'u64,
|
|
||||||
(1'u64 shl 28), (1'u64 shl 35) - 1'u64,
|
|
||||||
(1'u64 shl 35), (1'u64 shl 42) - 1'u64,
|
|
||||||
(1'u64 shl 42), (1'u64 shl 49) - 1'u64,
|
|
||||||
(1'u64 shl 49), (1'u64 shl 56) - 1'u64,
|
|
||||||
(1'u64 shl 56), (1'u64 shl 63) - 1'u64,
|
|
||||||
(1'u64 shl 63), 0xFFFF_FFFF_FFFF_FFFF'u64
|
|
||||||
]
|
|
||||||
const edgeSizes = [
|
|
||||||
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10
|
|
||||||
]
|
|
||||||
|
|
||||||
suite "Variable integer test suite":
|
|
||||||
|
|
||||||
test "vsizeof() edge cases test":
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
check vsizeof(edgeValues[i]) == edgeSizes[i]
|
|
||||||
|
|
||||||
test "Success edge cases test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
var value = 0'u64
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
buffer.setLen(edgeSizes[i])
|
|
||||||
check:
|
|
||||||
putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Success
|
|
||||||
value == edgeValues[i]
|
|
||||||
|
|
||||||
test "Buffer Overrun edge cases test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
buffer.setLen(edgeSizes[i] - 1)
|
|
||||||
let res = putUVarint(buffer, length, edgeValues[i])
|
|
||||||
check:
|
|
||||||
res == VarintStatus.Overrun
|
|
||||||
length == edgeSizes[i]
|
|
||||||
|
|
||||||
test "Buffer Incomplete edge cases test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
var value = 0'u64
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
buffer.setLen(edgeSizes[i])
|
|
||||||
check putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
buffer.setLen(len(buffer) - 1)
|
|
||||||
check:
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Incomplete
|
|
||||||
|
|
||||||
test "Integer Overflow 32bit test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
if edgeSizes[i] > 5:
|
|
||||||
var value = 0'u32
|
|
||||||
buffer.setLen(edgeSizes[i])
|
|
||||||
check:
|
|
||||||
putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Overflow
|
|
||||||
|
|
||||||
test "Integer Overflow 64bit test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
if edgeSizes[i] > 9:
|
|
||||||
var value = 0'u64
|
|
||||||
buffer.setLen(edgeSizes[i] + 1)
|
|
||||||
check:
|
|
||||||
putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
buffer[9] = buffer[9] or 0x80'u8
|
|
||||||
buffer[10] = 0x01'u8
|
|
||||||
check:
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Overflow
|
|
|
@ -1,81 +0,0 @@
|
||||||
import unittest
|
|
||||||
import ../libp2p/protobuf/varint
|
|
||||||
|
|
||||||
const edgeValues = [
|
|
||||||
0'u64, (1'u64 shl 7) - 1'u64,
|
|
||||||
(1'u64 shl 7), (1'u64 shl 14) - 1'u64,
|
|
||||||
(1'u64 shl 14), (1'u64 shl 21) - 1'u64,
|
|
||||||
(1'u64 shl 21), (1'u64 shl 28) - 1'u64,
|
|
||||||
(1'u64 shl 28), (1'u64 shl 35) - 1'u64,
|
|
||||||
(1'u64 shl 35), (1'u64 shl 42) - 1'u64,
|
|
||||||
(1'u64 shl 42), (1'u64 shl 49) - 1'u64,
|
|
||||||
(1'u64 shl 49), (1'u64 shl 56) - 1'u64,
|
|
||||||
(1'u64 shl 56), (1'u64 shl 63) - 1'u64,
|
|
||||||
(1'u64 shl 63), 0xFFFF_FFFF_FFFF_FFFF'u64
|
|
||||||
]
|
|
||||||
const edgeSizes = [
|
|
||||||
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10
|
|
||||||
]
|
|
||||||
|
|
||||||
suite "ProtoBuf's variable integer test suite":
|
|
||||||
|
|
||||||
test "vsizeof() edge cases test":
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
check vsizeof(edgeValues[i]) == edgeSizes[i]
|
|
||||||
|
|
||||||
test "Success edge cases test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
var value = 0'u64
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
buffer.setLen(edgeSizes[i])
|
|
||||||
check:
|
|
||||||
putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Success
|
|
||||||
value == edgeValues[i]
|
|
||||||
|
|
||||||
test "Buffer Overrun edge cases test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
buffer.setLen(edgeSizes[i] - 1)
|
|
||||||
let res = putUVarint(buffer, length, edgeValues[i])
|
|
||||||
check:
|
|
||||||
res == VarintStatus.Overrun
|
|
||||||
length == edgeSizes[i]
|
|
||||||
|
|
||||||
test "Buffer Incomplete edge cases test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
var value = 0'u64
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
buffer.setLen(edgeSizes[i])
|
|
||||||
check putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
buffer.setLen(len(buffer) - 1)
|
|
||||||
check:
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Incomplete
|
|
||||||
|
|
||||||
test "Integer Overflow 32bit test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
if edgeSizes[i] > 5:
|
|
||||||
var value = 0'u32
|
|
||||||
buffer.setLen(edgeSizes[i])
|
|
||||||
check:
|
|
||||||
putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Overflow
|
|
||||||
|
|
||||||
test "Integer Overflow 64bit test":
|
|
||||||
var buffer = newSeq[byte]()
|
|
||||||
var length = 0
|
|
||||||
for i in 0..<len(edgeValues):
|
|
||||||
if edgeSizes[i] > 9:
|
|
||||||
var value = 0'u64
|
|
||||||
buffer.setLen(edgeSizes[i] + 1)
|
|
||||||
check:
|
|
||||||
putUVarint(buffer, length, edgeValues[i]) == VarintStatus.Success
|
|
||||||
buffer[9] = buffer[9] or 0x80'u8
|
|
||||||
buffer[10] = 0x01'u8
|
|
||||||
check:
|
|
||||||
getUVarint(buffer, length, value) == VarintStatus.Overflow
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
import unittest
|
||||||
|
import ../libp2p/varint
|
||||||
|
|
||||||
|
const PBedgeValues = [
|
||||||
|
0'u64, (1'u64 shl 7) - 1'u64,
|
||||||
|
(1'u64 shl 7), (1'u64 shl 14) - 1'u64,
|
||||||
|
(1'u64 shl 14), (1'u64 shl 21) - 1'u64,
|
||||||
|
(1'u64 shl 21), (1'u64 shl 28) - 1'u64,
|
||||||
|
(1'u64 shl 28), (1'u64 shl 35) - 1'u64,
|
||||||
|
(1'u64 shl 35), (1'u64 shl 42) - 1'u64,
|
||||||
|
(1'u64 shl 42), (1'u64 shl 49) - 1'u64,
|
||||||
|
(1'u64 shl 49), (1'u64 shl 56) - 1'u64,
|
||||||
|
(1'u64 shl 56), (1'u64 shl 63) - 1'u64,
|
||||||
|
(1'u64 shl 63), 0xFFFF_FFFF_FFFF_FFFF'u64
|
||||||
|
]
|
||||||
|
|
||||||
|
const PBedgeExpects = [
|
||||||
|
"00", "7F",
|
||||||
|
"8001", "FF7F",
|
||||||
|
"808001", "FFFF7F",
|
||||||
|
"80808001", "FFFFFF7F",
|
||||||
|
"8080808001", "FFFFFFFF7F",
|
||||||
|
"808080808001", "FFFFFFFFFF7F",
|
||||||
|
"80808080808001", "FFFFFFFFFFFF7F",
|
||||||
|
"8080808080808001", "FFFFFFFFFFFFFF7F",
|
||||||
|
"808080808080808001", "FFFFFFFFFFFFFFFF7F",
|
||||||
|
"80808080808080808001", "FFFFFFFFFFFFFFFFFF01"
|
||||||
|
]
|
||||||
|
|
||||||
|
const PBedgeSizes = [
|
||||||
|
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10
|
||||||
|
]
|
||||||
|
|
||||||
|
const LPedgeValues = [
|
||||||
|
0'u64, (1'u64 shl 7) - 1'u64,
|
||||||
|
(1'u64 shl 7), (1'u64 shl 14) - 1'u64,
|
||||||
|
(1'u64 shl 14), (1'u64 shl 21) - 1'u64,
|
||||||
|
(1'u64 shl 21), (1'u64 shl 28) - 1'u64,
|
||||||
|
(1'u64 shl 28), (1'u64 shl 35) - 1'u64,
|
||||||
|
(1'u64 shl 35), (1'u64 shl 42) - 1'u64,
|
||||||
|
(1'u64 shl 42), (1'u64 shl 49) - 1'u64,
|
||||||
|
(1'u64 shl 49), (1'u64 shl 56) - 1'u64,
|
||||||
|
(1'u64 shl 56), (1'u64 shl 63) - 1'u64,
|
||||||
|
]
|
||||||
|
|
||||||
|
const LPedgeSizes = [
|
||||||
|
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9
|
||||||
|
]
|
||||||
|
|
||||||
|
const LPedgeExpects = [
|
||||||
|
"00", "7F",
|
||||||
|
"8001", "FF7F",
|
||||||
|
"808001", "FFFF7F",
|
||||||
|
"80808001", "FFFFFF7F",
|
||||||
|
"8080808001", "FFFFFFFF7F",
|
||||||
|
"808080808001", "FFFFFFFFFF7F",
|
||||||
|
"80808080808001", "FFFFFFFFFFFF7F",
|
||||||
|
"8080808080808001", "FFFFFFFFFFFFFF7F",
|
||||||
|
"808080808080808001", "FFFFFFFFFFFFFFFF7F",
|
||||||
|
]
|
||||||
|
|
||||||
|
proc hexChar*(c: byte, lowercase: bool = false): string =
|
||||||
|
var alpha: int
|
||||||
|
if lowercase:
|
||||||
|
alpha = ord('a')
|
||||||
|
else:
|
||||||
|
alpha = ord('A')
|
||||||
|
result = newString(2)
|
||||||
|
let t1 = ord(c) shr 4
|
||||||
|
let t0 = ord(c) and 0x0F
|
||||||
|
case t1
|
||||||
|
of 0..9: result[0] = chr(t1 + ord('0'))
|
||||||
|
else: result[0] = chr(t1 - 10 + alpha)
|
||||||
|
case t0:
|
||||||
|
of 0..9: result[1] = chr(t0 + ord('0'))
|
||||||
|
else: result[1] = chr(t0 - 10 + alpha)
|
||||||
|
|
||||||
|
proc toHex*(a: openarray[byte], lowercase: bool = false): string =
|
||||||
|
result = ""
|
||||||
|
for i in a:
|
||||||
|
result = result & hexChar(i, lowercase)
|
||||||
|
|
||||||
|
suite "Variable integer test suite":
|
||||||
|
|
||||||
|
test "vsizeof() edge cases test":
|
||||||
|
for i in 0..<len(PBedgeValues):
|
||||||
|
check vsizeof(PBedgeValues[i]) == PBedgeSizes[i]
|
||||||
|
|
||||||
|
test "[ProtoBuf] Success edge cases test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
var value = 0'u64
|
||||||
|
for i in 0..<len(PBedgeValues):
|
||||||
|
buffer.setLen(PBedgeSizes[i])
|
||||||
|
check:
|
||||||
|
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
|
||||||
|
PB.getUVarint(buffer, length, value) == VarintStatus.Success
|
||||||
|
value == PBedgeValues[i]
|
||||||
|
toHex(buffer) == PBedgeExpects[i]
|
||||||
|
|
||||||
|
test "[ProtoBuf] Buffer Overrun edge cases test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
for i in 0..<len(PBedgeValues):
|
||||||
|
buffer.setLen(PBedgeSizes[i] - 1)
|
||||||
|
let res = PB.putUVarint(buffer, length, PBedgeValues[i])
|
||||||
|
check:
|
||||||
|
res == VarintStatus.Overrun
|
||||||
|
length == PBedgeSizes[i]
|
||||||
|
|
||||||
|
test "[ProtoBuf] Buffer Incomplete edge cases test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
var value = 0'u64
|
||||||
|
for i in 0..<len(PBedgeValues):
|
||||||
|
buffer.setLen(PBedgeSizes[i])
|
||||||
|
check:
|
||||||
|
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
|
||||||
|
buffer.setLen(len(buffer) - 1)
|
||||||
|
check:
|
||||||
|
PB.getUVarint(buffer, length, value) == VarintStatus.Incomplete
|
||||||
|
|
||||||
|
test "[ProtoBuf] Integer Overflow 32bit test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
for i in 0..<len(PBedgeValues):
|
||||||
|
if PBedgeSizes[i] > 5:
|
||||||
|
var value = 0'u32
|
||||||
|
buffer.setLen(PBedgeSizes[i])
|
||||||
|
check:
|
||||||
|
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
|
||||||
|
PB.getUVarint(buffer, length, value) == VarintStatus.Overflow
|
||||||
|
|
||||||
|
test "[ProtoBuf] Integer Overflow 64bit test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
for i in 0..<len(PBedgeValues):
|
||||||
|
if PBedgeSizes[i] > 9:
|
||||||
|
var value = 0'u64
|
||||||
|
buffer.setLen(PBedgeSizes[i] + 1)
|
||||||
|
check:
|
||||||
|
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
|
||||||
|
buffer[9] = buffer[9] or 0x80'u8
|
||||||
|
buffer[10] = 0x01'u8
|
||||||
|
check:
|
||||||
|
PB.getUVarint(buffer, length, value) == VarintStatus.Overflow
|
||||||
|
|
||||||
|
test "[LibP2P] Success edge cases test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
var value = 0'u64
|
||||||
|
for i in 0..<len(LPedgeValues):
|
||||||
|
buffer.setLen(LPedgeSizes[i])
|
||||||
|
check:
|
||||||
|
LP.putUVarint(buffer, length, LPedgeValues[i]) == VarintStatus.Success
|
||||||
|
LP.getUVarint(buffer, length, value) == VarintStatus.Success
|
||||||
|
value == LPedgeValues[i]
|
||||||
|
toHex(buffer) == LPedgeExpects[i]
|
||||||
|
|
||||||
|
test "[LibP2P] Buffer Overrun edge cases test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
for i in 0..<len(LPedgeValues):
|
||||||
|
buffer.setLen(PBedgeSizes[i] - 1)
|
||||||
|
let res = LP.putUVarint(buffer, length, LPedgeValues[i])
|
||||||
|
check:
|
||||||
|
res == VarintStatus.Overrun
|
||||||
|
length == LPedgeSizes[i]
|
||||||
|
|
||||||
|
test "[LibP2P] Buffer Incomplete edge cases test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
var value = 0'u64
|
||||||
|
for i in 0..<len(LPedgeValues):
|
||||||
|
buffer.setLen(LPedgeSizes[i])
|
||||||
|
check:
|
||||||
|
LP.putUVarint(buffer, length, LPedgeValues[i]) == VarintStatus.Success
|
||||||
|
buffer.setLen(len(buffer) - 1)
|
||||||
|
check:
|
||||||
|
LP.getUVarint(buffer, length, value) == VarintStatus.Incomplete
|
||||||
|
|
||||||
|
test "[LibP2P] Integer Overflow 32bit test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
for i in 0..<len(LPedgeValues):
|
||||||
|
if LPedgeSizes[i] > 5:
|
||||||
|
var value = 0'u32
|
||||||
|
buffer.setLen(LPedgeSizes[i])
|
||||||
|
check:
|
||||||
|
LP.putUVarint(buffer, length, LPedgeValues[i]) == VarintStatus.Success
|
||||||
|
LP.getUVarint(buffer, length, value) == VarintStatus.Overflow
|
||||||
|
|
||||||
|
test "[LibP2P] Integer Overflow 64bit test":
|
||||||
|
var buffer = newSeq[byte]()
|
||||||
|
var length = 0
|
||||||
|
for i in 0..<len(LPedgeValues):
|
||||||
|
if LPedgeSizes[i] > 8:
|
||||||
|
var value = 0'u64
|
||||||
|
buffer.setLen(LPedgeSizes[i] + 1)
|
||||||
|
check:
|
||||||
|
LP.putUVarint(buffer, length, LPedgeValues[i]) == VarintStatus.Success
|
||||||
|
buffer[8] = buffer[8] or 0x80'u8
|
||||||
|
buffer[9] = 0x01'u8
|
||||||
|
check:
|
||||||
|
LP.getUVarint(buffer, length, value) == VarintStatus.Overflow
|
||||||
|
|
||||||
|
test "[LibP2P] Over 63bit test":
|
||||||
|
var buffer = newSeq[byte](10)
|
||||||
|
var length = 0
|
||||||
|
check:
|
||||||
|
LP.putUVarint(buffer, length,
|
||||||
|
0x7FFF_FFFF_FFFF_FFFF'u64) == VarintStatus.Success
|
||||||
|
LP.putUVarint(buffer, length,
|
||||||
|
0x8000_0000_0000_0000'u64) == VarintStatus.Overflow
|
||||||
|
LP.putUVarint(buffer, length,
|
||||||
|
0xFFFF_FFFF_FFFF_FFFF'u64) == VarintStatus.Overflow
|
Loading…
Reference in New Issue