nim-libp2p/tests/testvarint.nim

473 lines
16 KiB
Nim

{.used.}
# Nim-Libp2p
# Copyright (c) 2023 Status Research & Development GmbH
# 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.
import unittest2
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 PBPositiveSignedEdgeValues = [
0'u64, 0x3F'u64,
0x40'u64, 0x1FFF'u64,
0x2000'u64, 0xFFFFF'u64,
0x100000'u64, 0x7FFFFFF'u64,
0x8000000'u64, 0x3FFFFFFFF'u64,
0x400000000'u64, 0x1FFFFFFFFFF'u64,
0x20000000000'u64, 0xFFFFFFFFFFFF'u64,
0x1000000000000'u64, 0x7FFFFFFFFFFFFF'u64,
0x80000000000000'u64, 0x3FFFFFFFFFFFFFFF'u64,
0x4000000000000000'u64, 0x7FFFFFFFFFFFFFFF'u64
]
const PBNegativeSignedEdgeValues = [
0x0000000000000000'u64, 0xFFFFFFFFFFFFFFC0'u64,
0xFFFFFFFFFFFFFFBF'u64, 0xFFFFFFFFFFFFE000'u64,
0xFFFFFFFFFFFFDFFF'u64, 0xFFFFFFFFFFF00000'u64,
0xFFFFFFFFFFEFFFFF'u64, 0xFFFFFFFFF8000000'u64,
0xFFFFFFFFF7FFFFFF'u64, 0xFFFFFFFC00000000'u64,
0xFFFFFFFBFFFFFFFF'u64, 0xFFFFFE0000000000'u64,
0xFFFFFDFFFFFFFFFF'u64, 0xFFFF000000000000'u64,
0xFFFEFFFFFFFFFFFF'u64, 0xFF80000000000000'u64,
0xFF7FFFFFFFFFFFFF'u64, 0xC000000000000000'u64,
0xBFFFFFFFFFFFFFFF'u64, 0x8000000000000000'u64
]
const PBPositiveSignedZigZagEdgeExpects = [
"00", "7E",
"8001", "FE7F",
"808001", "FEFF7F",
"80808001", "FEFFFF7F",
"8080808001", "FEFFFFFF7F",
"808080808001", "FEFFFFFFFF7F",
"80808080808001", "FEFFFFFFFFFF7F",
"8080808080808001", "FEFFFFFFFFFFFF7F",
"808080808080808001", "FEFFFFFFFFFFFFFF7F",
"80808080808080808001", "FEFFFFFFFFFFFFFFFF01"
]
const PBNegativeSignedZigZagEdgeExpects = [
"00", "7F",
"8101", "FF7F",
"818001", "FFFF7F",
"81808001", "FFFFFF7F",
"8180808001", "FFFFFFFF7F",
"818080808001", "FFFFFFFFFF7F",
"81808080808001", "FFFFFFFFFFFF7F",
"8180808080808001", "FFFFFFFFFFFFFF7F",
"818080808080808001", "FFFFFFFFFFFFFFFF7F",
"81808080808080808001", "FFFFFFFFFFFFFFFFFF01",
]
const PBPositiveSignedEdgeExpects = [
"00", "3F",
"40", "FF3F",
"8040", "FFFF3F",
"808040", "FFFFFF3F",
"80808040", "FFFFFFFF3F",
"8080808040", "FFFFFFFFFF3F",
"808080808040", "FFFFFFFFFFFF3F",
"80808080808040", "FFFFFFFFFFFFFF3F",
"8080808080808040", "FFFFFFFFFFFFFFFF3F",
"808080808080808040", "FFFFFFFFFFFFFFFF7F"
]
const PBNegativeSignedEdgeExpects = [
"00", "C0FFFFFFFFFFFFFFFF01",
"BFFFFFFFFFFFFFFFFF01", "80C0FFFFFFFFFFFFFF01",
"FFBFFFFFFFFFFFFFFF01", "8080C0FFFFFFFFFFFF01",
"FFFFBFFFFFFFFFFFFF01", "808080C0FFFFFFFFFF01",
"FFFFFFBFFFFFFFFFFF01", "80808080C0FFFFFFFF01",
"FFFFFFFFBFFFFFFFFF01", "8080808080C0FFFFFF01",
"FFFFFFFFFFBFFFFFFF01", "808080808080C0FFFF01",
"FFFFFFFFFFFFBFFFFF01", "80808080808080C0FF01",
"FFFFFFFFFFFFFFBFFF01", "8080808080808080C001",
"FFFFFFFFFFFFFFFFBF01", "80808080808080808001"
]
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 PBEdgeSignedPositiveZigZagSizes = [
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10
]
const PBEdgeSignedNegativeZigZagSizes = [
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10
]
const PBEdgeSignedPositiveSizes = [
1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9
]
const PBEdgeSignedNegativeSizes = [
1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 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]
for i in 0 ..< len(PBPositiveSignedEdgeValues):
check:
vsizeof(hint64(PBPositiveSignedEdgeValues[i])) ==
PBEdgeSignedPositiveSizes[i]
vsizeof(zint64(PBPositiveSignedEdgeValues[i])) ==
PBEdgeSignedPositiveZigZagSizes[i]
for i in 0 ..< len(PBNegativeSignedEdgeValues):
check:
vsizeof(cast[hint64](PBNegativeSignedEdgeValues[i])) ==
PBEdgeSignedNegativeSizes[i]
vsizeof(cast[zint64](PBNegativeSignedEdgeValues[i])) ==
PBEdgeSignedNegativeZigZagSizes[i]
test "[ProtoBuf] Success edge cases test":
var buffer = newSeq[byte]()
var length = 0
var uvalue = 0'u64
var ivalue = hint64(0)
var svalue = zint64(0)
for i in 0 ..< len(PBedgeValues):
buffer.setLen(PBedgeSizes[i])
check:
PB.putUVarint(buffer, length, PBedgeValues[i]).isOk()
PB.getUVarint(buffer, length, uvalue).isOk()
uvalue == PBedgeValues[i]
toHex(buffer) == PBedgeExpects[i]
for i in 0 ..< len(PBPositiveSignedEdgeValues):
buffer.setLen(PBEdgeSignedPositiveSizes[i])
check:
putSVarint(buffer, length,
hint64(PBPositiveSignedEdgeValues[i])).isOk()
getSVarint(buffer, length, ivalue).isOk()
int64(ivalue) == int64(PBPositiveSignedEdgeValues[i])
toHex(buffer) == PBPositiveSignedEdgeExpects[i]
buffer.setLen(PBEdgeSignedPositiveZigZagSizes[i])
check:
putSVarint(buffer, length,
zint64(PBPositiveSignedEdgeValues[i])).isOk()
getSVarint(buffer, length, svalue).isOk()
int64(svalue) == int64(PBPositiveSignedEdgeValues[i])
toHex(buffer) == PBPositiveSignedZigZagEdgeExpects[i]
for i in 0 ..< len(PBNegativeSignedEdgeValues):
buffer.setLen(PBEdgeSignedNegativeSizes[i])
check:
putSVarint(buffer, length,
cast[hint64](PBNegativeSignedEdgeValues[i])).isOk()
getSVarint(buffer, length, ivalue).isOk()
int64(ivalue) == cast[int64](PBNegativeSignedEdgeValues[i])
toHex(buffer) == PBNegativeSignedEdgeExpects[i]
buffer.setLen(PBEdgeSignedNegativeZigZagSizes[i])
check:
putSVarint(buffer, length,
cast[zint64](PBNegativeSignedEdgeValues[i])).isOk()
getSVarint(buffer, length, svalue).isOk()
int64(svalue) == cast[int64](PBNegativeSignedEdgeValues[i])
toHex(buffer) == PBNegativeSignedZigZagEdgeExpects[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.error() == VarintError.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]).isOk()
buffer.setLen(buffer.high)
check:
PB.getUVarint(buffer, length, value).error() == VarintError.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]).isOk()
PB.getUVarint(buffer, length, value).error() == VarintError.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]).isOk()
buffer[9] = buffer[9] or 0x80'u8
buffer[10] = 0x01'u8
check:
PB.getUVarint(buffer, length, value).error() == VarintError.Overflow
test "[ProtoBuf] Test vectors":
# The test vectors which was obtained at:
# https://github.com/dermesser/integer-encoding-rs/blob/master/src/varint_tests.rs
# https://github.com/That3Percent/zigzag/blob/master/src/lib.rs
check:
PB.encodeVarint(0'u64).get() == @[0x00'u8]
PB.encodeVarint(0'u32).get() == @[0x00'u8]
PB.encodeVarint(hint64(0)).get() == @[0x00'u8]
PB.encodeVarint(hint32(0)).get() == @[0x00'u8]
PB.encodeVarint(zint64(0)).get() == @[0x00'u8]
PB.encodeVarint(zint32(0)).get() == @[0x00'u8]
PB.encodeVarint(zint32(-1)).get() == PB.encodeVarint(1'u32).get()
PB.encodeVarint(zint64(150)).get( ) == PB.encodeVarint(300'u32).get()
PB.encodeVarint(zint64(-150)).get() == PB.encodeVarint(299'u32).get()
PB.encodeVarint(zint32(-2147483648)).get() == PB.encodeVarint(4294967295'u64).get()
PB.encodeVarint(zint32(2147483647)).get() == PB.encodeVarint(4294967294'u64).get()
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]).isOk()
LP.getUVarint(buffer, length, value).isOk()
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.error() == VarintError.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]).isOk()
buffer.setLen(buffer.high)
check:
LP.getUVarint(buffer, length, value).error() == VarintError.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]).isOk()
LP.getUVarint(buffer, length, value).error() == VarintError.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]).isOk()
buffer[8] = buffer[8] or 0x80'u8
buffer[9] = 0x01'u8
check:
LP.getUVarint(buffer, length, value).error() == VarintError.Overflow
test "[LibP2P] Over 63bit test":
var buffer = newSeq[byte](10)
var length = 0
check:
LP.putUVarint(buffer, length,
0x7FFF_FFFF_FFFF_FFFF'u64).isOk()
LP.putUVarint(buffer, length,
0x8000_0000_0000_0000'u64).error() == VarintError.Overflow
LP.putUVarint(buffer, length,
0xFFFF_FFFF_FFFF_FFFF'u64).error() == VarintError.Overflow
test "[LibP2P] Overlong values test":
const OverlongValues = [
# Zero bytes at the end
@[0x81'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8,
0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8,
0x80'u8, 0x00'u8],
# Zero bytes at the middle and zero byte at the end
@[0x81'u8, 0x80'u8, 0x81'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x81'u8,
0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8,
0x81'u8, 0x00'u8],
# Zero bytes at the middle and zero bytes at the end
@[0x81'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x80'u8, 0x80'u8, 0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x80'u8, 0x80'u8,
0x00'u8],
@[0x81'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x80'u8, 0x81'u8, 0x80'u8,
0x80'u8, 0x00'u8],
]
var length = 0
var value = 0'u64
for item in OverlongValues:
check:
LP.getUVarint(item, length, value).error() == VarintError.Overlong
length == 0
value == 0
# We still should be able to decode zero value
check:
LP.getUVarint(@[0x00'u8], length, value).isOk()
length == 1
value == 0
# But not overlonged zero value
check:
LP.getUVarint(@[0x80'u8, 0x00'u8], length, value).error() == VarintError.Overlong
length == 0
value == 0
test "getVarint/putVarint tests":
proc `==`(a, b: zint32|hint32): bool =
int32(a) == int32(b)
proc `==`(a, b: zint64|hint64): bool =
int64(a) == int64(b)
template varintTest(ttype, vtype, value, expect: untyped) =
var ovalue: vtype
var buffer = newSeq[byte](10)
var length = 0
check ttype.putVarint(buffer, length, value).isOk()
buffer.setLen(length)
check:
toHex(buffer) == expect
ttype.getVarint(buffer, length, ovalue).isOk()
ovalue == value
varintTest(PB, uint64, high(uint64), "FFFFFFFFFFFFFFFFFF01")
varintTest(PB, uint32, high(uint32), "FFFFFFFF0F")
varintTest(PB, zint64, zint64(high(int64)), "FEFFFFFFFFFFFFFFFF01")
varintTest(PB, zint32, zint32(high(int32)), "FEFFFFFF0F")
varintTest(PB, zint64, zint64(low(int64)), "FFFFFFFFFFFFFFFFFF01")
varintTest(PB, zint32, zint32(low(int32)), "FFFFFFFF0F")
varintTest(PB, hint64, hint64(high(int64)), "FFFFFFFFFFFFFFFF7F")
varintTest(PB, hint32, hint32(high(int32)), "FFFFFFFF07")
varintTest(PB, hint64, hint64(low(int64)), "80808080808080808001")
varintTest(PB, hint32, hint32(low(int32)), "8080808008")
varintTest(LP, uint64, uint64(high(int64)), "FFFFFFFFFFFFFFFF7F")
varintTest(LP, uint32, uint32(high(uint32)), "FFFFFFFF0F")
varintTest(LP, uint16, uint16(high(uint16)), "FFFF03")
varintTest(LP, uint8, uint8(high(uint8)), "FF01")