diff --git a/libp2p.nimble b/libp2p.nimble index c0f2b14..bb93ae4 100644 --- a/libp2p.nimble +++ b/libp2p.nimble @@ -11,5 +11,5 @@ requires "nim > 0.18.0", "https://github.com/status-im/nim-asyncdispatch2" task test, "Runs the test suite": - exec "nim c -r tests/testpbvarint" + exec "nim c -r tests/testvarint" exec "nim c -r tests/testdaemon" \ No newline at end of file diff --git a/libp2p/daemon/daemonapi.nim b/libp2p/daemon/daemonapi.nim index a5f0fac..49549d6 100644 --- a/libp2p/daemon/daemonapi.nim +++ b/libp2p/daemon/daemonapi.nim @@ -10,7 +10,7 @@ ## This module implementes API for `go-libp2p-daemon`. import os, osproc, strutils, tables, streams import asyncdispatch2 -import ../protobuf/varint, ../protobuf/minprotobuf, transpool +import ../varint, ../protobuf/minprotobuf, transpool when not defined(windows): import posix @@ -367,7 +367,7 @@ proc recvMessage(conn: StreamTransport): Future[seq[byte]] {.async.} = var buffer = newSeq[byte](10) for i in 0.. MaxMessageSize: @@ -852,15 +852,3 @@ proc dhtSearchValue*(api: DaemonAPI, key: string, result = list finally: 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() diff --git a/libp2p/protobuf/minprotobuf.nim b/libp2p/protobuf/minprotobuf.nim index 6a32afb..feaf1a7 100644 --- a/libp2p/protobuf/minprotobuf.nim +++ b/libp2p/protobuf/minprotobuf.nim @@ -8,7 +8,7 @@ ## those terms. ## This module implements minimal Google's ProtoBuf primitives. -import varint +import ../varint const MaxMessageSize* = 1'u shl 22 @@ -134,12 +134,12 @@ proc write*(pb: var ProtoBuffer, field: ProtoField) = var length = 0 var res: VarintStatus 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) pb.offset += length case field.kind of ProtoFieldKind.Varint: - res = putUVarint(pb.toOpenArray(), length, field.vint) + res = PB.putUVarint(pb.toOpenArray(), length, field.vint) assert(res == VarintStatus.Success) pb.offset += length 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.offset += 4 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) pb.offset += length assert(pb.isEnough(len(field.vbuffer))) @@ -180,7 +180,7 @@ proc finish*(pb: var ProtoBuffer) = if WithVarintLength in pb.options: let size = uint(len(pb.buffer) - 10) 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) pb.offset = pos else: @@ -194,14 +194,14 @@ proc getVarintValue*(data: var ProtoBuffer, field: int, var soffset = data.offset if not data.isEmpty() and - getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success: + PB.getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success: data.offset += length if header == protoHeader(field, Varint): if not data.isEmpty(): when type(value) is int32 or type(value) is int64 or type(value) is int: let res = getSVarint(data.toOpenArray(), length, value) else: - let res = getUVarint(data.toOpenArray(), length, value) + let res = PB.getUVarint(data.toOpenArray(), length, value) data.offset += length result = length return @@ -218,11 +218,11 @@ proc getLengthValue*[T: string|seq[byte]](data: var ProtoBuffer, field: int, result = -1 buffer.setLen(0) if not data.isEmpty() and - getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success: + PB.getUVarint(data.toOpenArray(), length, header) == VarintStatus.Success: data.offset += length if header == protoHeader(field, Length): if not data.isEmpty() and - getUVarint(data.toOpenArray(), length, ssize) == VarintStatus.Success: + PB.getUVarint(data.toOpenArray(), length, ssize) == VarintStatus.Success: data.offset += length if ssize <= MaxMessageSize and data.isEnough(int(ssize)): buffer.setLen(ssize) @@ -255,11 +255,11 @@ proc enterSubmessage*(pb: var ProtoBuffer): int = var soffset = pb.offset if not pb.isEmpty() and - getUVarint(pb.toOpenArray(), length, header) == VarintStatus.Success: + PB.getUVarint(pb.toOpenArray(), length, header) == VarintStatus.Success: pb.offset += length if (header and 0x07'u64) == cast[uint64](ProtoFieldKind.Length): if not pb.isEmpty() and - getUVarint(pb.toOpenArray(), length, msize) == VarintStatus.Success: + PB.getUVarint(pb.toOpenArray(), length, msize) == VarintStatus.Success: pb.offset += length if msize <= MaxMessageSize and pb.isEnough(int(msize)): pb.length = int(msize) diff --git a/libp2p/protobuf/varint.nim b/libp2p/varint.nim similarity index 55% rename from libp2p/protobuf/varint.nim rename to libp2p/varint.nim index d354310..e264db6 100644 --- a/libp2p/protobuf/varint.nim +++ b/libp2p/varint.nim @@ -7,7 +7,12 @@ ## This file may not be copied, modified, or distributed except according to ## 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 type @@ -18,20 +23,31 @@ type Incomplete, Overrun - SomeUVarint* = uint | uint64 | uint32 - SomeSVarint* = int | int64 | int32 - SomeVarint* = SomeUVarint | SomeSVarint + PB* = object + ## Use this type to specify Google ProtoBuf's varint encoding + 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 -proc vsizeof*(x: SomeUVarint|SomeSVarint): int {.inline.} = +proc vsizeof*(x: SomeVarint): int {.inline.} = ## Returns number of bytes required to encode integer ``x`` as varint. if x == cast[type(x)](0): result = 1 else: result = (fastLog2(x) + 1 + 7 - 1) div 7 -proc getUVarint*(pbytes: openarray[byte], outlen: var int, - outval: var SomeUVarint): VarintStatus = +proc getUVarint*[T: PB|LP](vtype: typedesc[T], + pbytes: openarray[byte], + outlen: var int, + outval: var SomeUVarint): VarintStatus = ## Decode `unsigned varint` from buffer ``pbytes`` and store it to ``outval``. ## On success ``outlen`` will be set to number of bytes processed while ## decoding `unsigned varint`. @@ -44,11 +60,23 @@ proc getUVarint*(pbytes: openarray[byte], outlen: var int, ## If encoded value can produce integer overflow, ``Overflow`` error will be ## returned. ## - ## Note, when decoding 10th byte of 64bit integer only 1 bit from byte will be - ## decoded, all other bits will be ignored. When decoding 5th byte of 32bit - ## integer only 4 bits from byte will be decoded, all other bits will be - ## ignored. - const MaxBits = byte(sizeof(outval) * 8) + ## Google ProtoBuf + ## When decoding 10th byte of Google Protobuf's 64bit integer only 1 bit from + ## byte will be decoded, all other bits will be ignored. When decoding 5th + ## 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) + else: + when sizeof(outval) == 8: + const MaxBits = 63'u8 + else: + const MaxBits = byte(sizeof(outval) * 8) + var shift = 0'u8 result = VarintStatus.Incomplete outlen = 0 @@ -67,13 +95,14 @@ proc getUVarint*(pbytes: openarray[byte], outlen: var int, if (b and 0x80'u8) == 0'u8: result = VarintStatus.Success break - if result == VarintStatus.Incomplete: outlen = 0 outval = cast[type(outval)](0) -proc putUVarint*(pbytes: var openarray[byte], outlen: var int, - outval: SomeUVarint): VarintStatus = +proc putUVarint*[T: PB|LP](vtype: typedesc[T], + pbytes: var openarray[byte], + outlen: var int, + outval: SomeUVarint): VarintStatus = ## Encode `unsigned varint` ``outval`` and store it to array ``pbytes``. ## ## On success ``outlen`` will hold number of bytes (octets) used to encode @@ -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 ## required. ## + ## Google ProtoBuf ## Maximum encoded length of 64bit integer is 10 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 value = outval 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): buffer[0] = cast[byte](outval and 0xFF) inc(k) @@ -107,10 +147,10 @@ proc putUVarint*(pbytes: var openarray[byte], outlen: var int, result = VarintStatus.Overrun proc getSVarint*(pbytes: openarray[byte], outsize: var int, - outval: var SomeSVarint): VarintStatus {.inline.} = - ## Decode `signed varint` from buffer ``pbytes`` and store it to ``outval``. - ## On success ``outlen`` will be set to number of bytes processed while - ## decoding `signed varint`. + outval: var PBSomeSVarint): VarintStatus {.inline.} = + ## Decode Google ProtoBuf's `signed varint` from buffer ``pbytes`` and store + ## it to ``outval``. On success ``outlen`` will be set to number of bytes + ## processed while decoding `signed varint`. ## ## If array ``pbytes`` is empty, ``Incomplete`` error will be returned. ## @@ -129,7 +169,7 @@ proc getSVarint*(pbytes: openarray[byte], outsize: var int, else: var value: uint32 - result = getUVarint(pbytes, outsize, value) + result = PB.getUVarint(pbytes, outsize, value) if result == VarintStatus.Success: if (value and cast[type(value)](1)) != cast[type(value)](0): 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) proc putSVarint*(pbytes: var openarray[byte], outsize: var int, - outval: SomeSVarint): VarintStatus {.inline.} = - ## Encode `signed varint` ``outval`` and store it to array ``pbytes``. + outval: PBSomeSVarint): VarintStatus {.inline.} = + ## 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 ## unsigned integer ``v``. @@ -161,11 +202,12 @@ proc putSVarint*(pbytes: var openarray[byte], outsize: var int, not(cast[uint32](outval) shl 1) else: cast[uint32](outval) shl 1 - result = putUVarint(pbytes, outsize, value) + result = PB.putUVarint(pbytes, outsize, value) -proc encodeVarint*(value: SomeUVarint|SomeSVarint): seq[byte] {.inline.} = - ## Encode integer to `signed/unsigned varint` and returns sequence of bytes - ## as result. +proc encodeVarint*(vtype: typedesc[PB], + value: PBSomeVarint): seq[byte] {.inline.} = + ## Encode integer to Google ProtoBuf's `signed/unsigned varint` and returns + ## sequence of bytes as result. var outsize = 0 result = newSeqOfCap[byte](10) when sizeof(value) == 4: @@ -181,6 +223,26 @@ proc encodeVarint*(value: SomeUVarint|SomeSVarint): seq[byte] {.inline.} = else: 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.} = ## Decode signed integer from array ``data`` and return it as result. var outsize = 0 @@ -188,91 +250,10 @@ proc decodeSVarint*(data: openarray[byte]): int {.inline.} = if res != VarintStatus.Success: 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. var outsize = 0 - let res = getUVarint(data, outsize, result) + let res = vtype.getUVarint(data, outsize, result) if res != VarintStatus.Success: 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.. 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.. 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 diff --git a/tests/testpbvarint.nim b/tests/testpbvarint.nim deleted file mode 100644 index 53597d3..0000000 --- a/tests/testpbvarint.nim +++ /dev/null @@ -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.. 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.. 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 diff --git a/tests/testvarint.nim b/tests/testvarint.nim new file mode 100644 index 0000000..c601853 --- /dev/null +++ b/tests/testvarint.nim @@ -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.. 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.. 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.. 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.. 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