First steps to get Result[T].

This commit is contained in:
cheatfate 2019-08-04 11:18:23 +03:00
parent dfd824bd03
commit c9b5af87a0
No known key found for this signature in database
GPG Key ID: 46ADD633A7201F95
6 changed files with 747 additions and 193 deletions

View File

@ -9,13 +9,15 @@
## This module implements BASE58 encoding and decoding procedures.
## This module supports two variants of BASE58 encoding (Bitcoin and Flickr).
import result
import errors as e
type
Base58Status* {.pure.} = enum
Error,
Success,
Incorrect,
Overrun
# Base58Status* {.pure.} = enum
# Error,
# Success,
# Incorrect,
# Overrun
Base58Alphabet* = object
decode*: array[128, int8]
@ -59,13 +61,10 @@ proc decodedLength*(btype: typedesc[Base58C], length: int): int =
result = length + 4
proc encode*(btype: typedesc[Base58C], inbytes: openarray[byte],
outstr: var openarray[char], outlen: var int): Base58Status =
outstr: var openarray[char]): Result[int, e.Error] =
## Encode array of bytes ``inbytes`` using BASE58 encoding and store
## result to ``outstr``. On success ``Base58Status.Success`` will be returned
## and ``outlen`` will be set to number of characters stored inside of
## ``outstr``. If length of ``outstr`` is not enough then
## ``Base58Status.Overrun`` will be returned and ``outlen`` will be set to
## number of characters required.
## result to ``outstr``. On success procedure returns number of characters
## stored inside ``outstr``.
when btype is BTCBase58:
const alphabet = BTCAlphabet
elif btype is FLCBase58:
@ -99,9 +98,8 @@ proc encode*(btype: typedesc[Base58C], inbytes: openarray[byte],
inc(j)
let needed = zcount + size - j
outlen = needed
if len(outstr) < needed:
result = Base58Status.Overrun
result.err(e.OverrunError)
else:
for k in 0..<zcount:
outstr[k] = cast[char](alphabet.encode[0])
@ -110,48 +108,109 @@ proc encode*(btype: typedesc[Base58C], inbytes: openarray[byte],
outstr[i] = cast[char](alphabet.encode[buffer[j]])
inc(j)
inc(i)
result = Base58Status.Success
result.ok(needed)
# proc encode*(btype: typedesc[Base58C], inbytes: openarray[byte],
# outstr: var openarray[char], outlen: var int): Base58Status =
# ## Encode array of bytes ``inbytes`` using BASE58 encoding and store
# ## result to ``outstr``. On success ``Base58Status.Success`` will be returned
# ## and ``outlen`` will be set to number of characters stored inside of
# ## ``outstr``. If length of ``outstr`` is not enough then
# ## ``Base58Status.Overrun`` will be returned and ``outlen`` will be set to
# ## number of characters required.
# when btype is BTCBase58:
# const alphabet = BTCAlphabet
# elif btype is FLCBase58:
# const alphabet = FlickrAlphabet
# let binsz = len(inbytes)
# var zcount = 0
# while zcount < binsz and inbytes[zcount] == 0x00'u8:
# inc(zcount)
# let size = ((binsz - zcount) * 138) div 100 + 1
# var buffer = newSeq[uint8](size)
# var hi = size - 1
# var i = zcount
# var j = size - 1
# while i < binsz:
# var carry = uint32(inbytes[i])
# j = size - 1
# while (j > hi) or (carry != 0'u32):
# carry = carry + uint32(256'u32 * buffer[j])
# buffer[j] = cast[byte](carry mod 58)
# carry = carry div 58
# dec(j)
# hi = j
# inc(i)
# j = 0
# while (j < size) and (buffer[j] == 0x00'u8):
# inc(j)
# let needed = zcount + size - j
# outlen = needed
# if len(outstr) < needed:
# result = Base58Status.Overrun
# else:
# for k in 0..<zcount:
# outstr[k] = cast[char](alphabet.encode[0])
# i = zcount
# while j < size:
# outstr[i] = cast[char](alphabet.encode[buffer[j]])
# inc(j)
# inc(i)
# result = Base58Status.Success
# proc encode*(btype: typedesc[Base58C],
# inbytes: openarray[byte]): string {.inline.} =
# ## Encode array of bytes ``inbytes`` using BASE58 encoding and return
# ## encoded string.
# var size = (len(inbytes) * 138) div 100 + 1
# result = newString(size)
# if btype.encode(inbytes, result.toOpenArray(0, size - 1),
# size) == Base58Status.Success:
# result.setLen(size)
# else:
# result = ""
proc encode*(btype: typedesc[Base58C],
inbytes: openarray[byte]): string {.inline.} =
inbytes: openarray[byte]): Result[string, e.Error] {.inline.} =
## Encode array of bytes ``inbytes`` using BASE58 encoding and return
## encoded string.
var size = (len(inbytes) * 138) div 100 + 1
result = newString(size)
if btype.encode(inbytes, result.toOpenArray(0, size - 1),
size) == Base58Status.Success:
result.setLen(size)
var resstr = newString(size)
let res = btype.encode(inbytes, resstr.toOpenArray(0, size - 1))
if res.isOk:
resstr.setLen(res.get())
result.ok(resstr)
else:
result = ""
result.err(res.error)
proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
outbytes: var openarray[byte], outlen: var int): Base58Status =
## Decode BASE58 string and store array of bytes to ``outbytes``. On success
## ``Base58Status.Success`` will be returned and ``outlen`` will be set
## to number of bytes stored.
outbytes: var openarray[byte]): Result[int, e.Error] =
## Decode BASE58 encoded string and store result to array of bytes
## ``outbytes``. On success procedure returns number of bytes stored inside
## of ``outbytes``.
##
## Length of ``outbytes`` must be equal or more then ``len(instr) + 4``.
##
## If ``instr`` has characters which are not part of BASE58 alphabet, then
## ``Base58Status.Incorrect`` will be returned and ``outlen`` will be set to
## ``0``.
##
## If length of ``outbytes`` is not enough to store decoded bytes, then
## ``Base58Status.Overrun`` will be returned and ``outlen`` will be set to
## number of bytes required.
when btype is BTCBase58:
const alphabet = BTCAlphabet
elif btype is FLCBase58:
const alphabet = FlickrAlphabet
var outlen = 0
if len(instr) == 0:
outlen = 0
return Base58Status.Success
result.ok(0)
return
let binsz = len(instr) + 4
if len(outbytes) < binsz:
outlen = binsz
return Base58Status.Overrun
result.err(e.OverrunError)
return
var bytesleft = binsz mod 4
var zeromask: uint32
@ -167,13 +226,11 @@ proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
for i in zcount..<len(instr):
if (cast[byte](instr[i]) and 0x80'u8) != 0:
outlen = 0
result = Base58Status.Incorrect
result.err(e.IncorrectEncodingError)
return
let ch = alphabet.decode[int8(instr[i])]
if ch == -1:
outlen = 0
result = Base58Status.Incorrect
result.err(e.IncorrectEncodingError)
return
var c = cast[uint32](ch)
for j in countdown(size - 1, 0):
@ -181,12 +238,10 @@ proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
c = cast[uint32]((t and 0x3F_0000_0000'u64) shr 32)
buffer[j] = cast[uint32](t and 0xFFFF_FFFF'u32)
if c != 0:
outlen = 0
result = Base58Status.Incorrect
result.err(e.IncorrectEncodingError)
return
if (buffer[0] and zeromask) != 0:
outlen = 0
result = Base58Status.Incorrect
result.err(e.IncorrectEncodingError)
return
var boffset = 0
@ -217,7 +272,7 @@ proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
while m < binsz:
if outbytes[m] != 0x00:
if zcount > m:
result = Base58Status.Overrun
result.err(e.OverrunError)
return
break
inc(m)
@ -226,14 +281,131 @@ proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
if m < binsz:
moveMem(addr outbytes[zcount], addr outbytes[binsz - outlen], outlen)
outlen += zcount
result = Base58Status.Success
result.ok(outlen)
proc decode*(btype: typedesc[Base58C], instr: string): seq[byte] =
# proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
# outbytes: var openarray[byte], outlen: var int): Base58Status =
# ## Decode BASE58 string and store array of bytes to ``outbytes``. On success
# ## ``Base58Status.Success`` will be returned and ``outlen`` will be set
# ## to number of bytes stored.
# ##
# ## Length of ``outbytes`` must be equal or more then ``len(instr) + 4``.
# ##
# ## If ``instr`` has characters which are not part of BASE58 alphabet, then
# ## ``Base58Status.Incorrect`` will be returned and ``outlen`` will be set to
# ## ``0``.
# ##
# ## If length of ``outbytes`` is not enough to store decoded bytes, then
# ## ``Base58Status.Overrun`` will be returned and ``outlen`` will be set to
# ## number of bytes required.
# when btype is BTCBase58:
# const alphabet = BTCAlphabet
# elif btype is FLCBase58:
# const alphabet = FlickrAlphabet
# if len(instr) == 0:
# outlen = 0
# return Base58Status.Success
# let binsz = len(instr) + 4
# if len(outbytes) < binsz:
# outlen = binsz
# return Base58Status.Overrun
# var bytesleft = binsz mod 4
# var zeromask: uint32
# if bytesleft != 0:
# zeromask = cast[uint32](0xFFFF_FFFF'u32 shl (bytesleft * 8))
# let size = (binsz + 3) div 4
# var buffer = newSeq[uint32](size)
# var zcount = 0
# while zcount < len(instr) and instr[zcount] == cast[char](alphabet.encode[0]):
# inc(zcount)
# for i in zcount..<len(instr):
# if (cast[byte](instr[i]) and 0x80'u8) != 0:
# outlen = 0
# result = Base58Status.Incorrect
# return
# let ch = alphabet.decode[int8(instr[i])]
# if ch == -1:
# outlen = 0
# result = Base58Status.Incorrect
# return
# var c = cast[uint32](ch)
# for j in countdown(size - 1, 0):
# let t = cast[uint64](buffer[j]) * 58 + c
# c = cast[uint32]((t and 0x3F_0000_0000'u64) shr 32)
# buffer[j] = cast[uint32](t and 0xFFFF_FFFF'u32)
# if c != 0:
# outlen = 0
# result = Base58Status.Incorrect
# return
# if (buffer[0] and zeromask) != 0:
# outlen = 0
# result = Base58Status.Incorrect
# return
# var boffset = 0
# var joffset = 0
# if bytesleft == 3:
# outbytes[boffset] = cast[uint8]((buffer[0] and 0xFF_0000'u32) shr 16)
# inc(boffset)
# bytesleft = 2
# if bytesleft == 2:
# outbytes[boffset] = cast[uint8]((buffer[0] and 0xFF00'u32) shr 8)
# inc(boffset)
# bytesleft = 1
# if bytesleft == 1:
# outbytes[boffset] = cast[uint8]((buffer[0] and 0xFF'u32))
# inc(boffset)
# joffset = 1
# while joffset < size:
# outbytes[boffset + 0] = cast[byte]((buffer[joffset] shr 0x18) and 0xFF)
# outbytes[boffset + 1] = cast[byte]((buffer[joffset] shr 0x10) and 0xFF)
# outbytes[boffset + 2] = cast[byte]((buffer[joffset] shr 0x8) and 0xFF)
# outbytes[boffset + 3] = cast[byte](buffer[joffset] and 0xFF)
# boffset += 4
# inc(joffset)
# outlen = binsz
# var m = 0
# while m < binsz:
# if outbytes[m] != 0x00:
# if zcount > m:
# result = Base58Status.Overrun
# return
# break
# inc(m)
# dec(outlen)
# if m < binsz:
# moveMem(addr outbytes[zcount], addr outbytes[binsz - outlen], outlen)
# outlen += zcount
# result = Base58Status.Success
# proc decode*(btype: typedesc[Base58C], instr: string): seq[byte] =
# ## Decode BASE58 string ``instr`` and return sequence of bytes as result.
# if len(instr) > 0:
# var size = len(instr) + 4
# result = newSeq[byte](size)
# if btype.decode(instr, result, size) == Base58Status.Success:
# result.setLen(size)
# else:
# raise newException(Base58Error, "Incorrect base58 string")
proc decode*(btype: typedesc[Base58C],
instr: string): Result[seq[byte], e.Error] =
## Decode BASE58 string ``instr`` and return sequence of bytes as result.
if len(instr) > 0:
var size = len(instr) + 4
result = newSeq[byte](size)
if btype.decode(instr, result, size) == Base58Status.Success:
result.setLen(size)
var resbytes = newSeq[byte](size)
let res = btype.decode(instr, resbytes)
if res.isOk:
resbytes.setLen(res.value)
result.ok(resbytes)
else:
raise newException(Base58Error, "Incorrect base58 string")
result.err(res.error)

33
libp2p/errors.nim Normal file
View File

@ -0,0 +1,33 @@
## Nim-Libp2p
## Copyright (c) 2018 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 result
export result
type
Error* = enum
NoError,
GenericError,
IncorrectError,
OverrunError,
OverflowError,
IncompleteError,
EndOfBufferError,
NoSupportError,
VarintError,
IncorrectEncodingError,
MultiAddressMalformedError,
MultiAddressDecodeError,
MultiAddressEncodeError,
MultiAddressProtocolNotFoundError,
MultiAddressProtocolIncorrectError,
MultiAddressIncorrectError,
MultiAddressNoSupportError,

View File

@ -14,6 +14,7 @@
## - 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 errors as e
type
VarintStatus* {.pure.} = enum
@ -37,6 +38,10 @@ type
SomeUVarint* = PBSomeUVarint | LPSomeUVarint
VarintError* = object of CatchableError
Varint* = object
value*: uint64
length*: int8
proc vsizeof*(x: SomeVarint): int {.inline.} =
## Returns number of bytes required to encode integer ``x`` as varint.
if x == cast[type(x)](0):
@ -44,6 +49,36 @@ proc vsizeof*(x: SomeVarint): int {.inline.} =
else:
result = (fastLog2(x) + 1 + 7 - 1) div 7
proc getUVarint*[T: PB|LP](vt: typedesc[T],
pbytes: openarray[byte]): Result[Varint, e.Error] =
when vt is PB:
const MaxBits = 64'u8
else:
const MaxBits = 63'u8
var status = e.IncompleteError
var shift = 0'u8
var outlen = 0'i8
var outval = 0'u64
for i in 0..<len(pbytes):
let b = pbytes[i]
if shift >= MaxBits:
status = e.OverflowError
break
else:
outval = outval or (cast[type(outval)](b and 0x7F'u8) shl shift)
shift += 7
inc(outlen)
if (b and 0x80'u8) == 0'u8:
status = e.NoError
break
if status == e.NoError:
result.ok(Varint(value: outval, length: outlen))
else:
result.err(status)
proc getUVarint*[T: PB|LP](vtype: typedesc[T],
pbytes: openarray[byte],
outlen: var int,
@ -99,6 +134,34 @@ proc getUVarint*[T: PB|LP](vtype: typedesc[T],
outlen = 0
outval = cast[type(outval)](0)
proc putUVarint*[T: PB|LP](vt: typedesc[T], pbytes: var openarray[byte],
value: SomeUVarint): Result[int, e.Error] =
## Returns number of bytes used to encode value ``value``.
var buffer: array[10, byte]
var k = 0
var v = value
when vt is LP:
if sizeof(value) == 8 and (value and 0x8000_0000_0000_0000'u64) != 0'u64:
result.err(e.OverflowError)
return
if v <= cast[type(value)](0x7F):
buffer[0] = cast[byte](value and 0xFF)
inc(k)
else:
while v != cast[type(value)](0):
buffer[k] = cast[byte]((v and 0x7F) or 0x80)
v = v shr 7
inc(k)
buffer[k - 1] = buffer[k - 1] and 0x7F'u8
if len(pbytes) >= k:
copyMem(addr pbytes[0], addr buffer[0], k)
result.ok(k)
else:
result.err(e.OverrunError)
proc putUVarint*[T: PB|LP](vtype: typedesc[T],
pbytes: var openarray[byte],
outlen: var int,
@ -146,6 +209,19 @@ proc putUVarint*[T: PB|LP](vtype: typedesc[T],
else:
result = VarintStatus.Overrun
proc getSVarint*(vt: typedesc[PB],
pbytes: openarray[byte]): Result[Varint, e.Error] {.inline.} =
let res = PB.getUVarint(pbytes)
if res.isErr():
result.err(res.error)
else:
var value = res.value.value
if (value and 1'u64) != 0'u64:
value = not(value shr 1)
else:
value = value shr 1
result.ok(Varint(value: value, length: res.value.length))
proc getSVarint*(pbytes: openarray[byte], outsize: var int,
outval: var PBSomeSVarint): VarintStatus {.inline.} =
## Decode Google ProtoBuf's `signed varint` from buffer ``pbytes`` and store
@ -176,6 +252,22 @@ proc getSVarint*(pbytes: openarray[byte], outsize: var int,
else:
outval = cast[type(outval)](value shr 1)
proc putSVarint*(vt: typedesc[PB], pbytes: var openarray[byte],
value: PBSomeSVarint): Result[int, e.Error] {.inline.} =
when sizeof(outval) == 8:
var v: uint64 =
if value < 0:
not(cast[uint64](outval) shl 1)
else:
cast[uint64](outval) shl 1
else:
var v: uint32 =
if outval < 0:
not(cast[uint32](outval) shl 1)
else:
cast[uint32](outval) shl 1
result = PB.putUVarint(pbytes, v)
proc putSVarint*(pbytes: var openarray[byte], outsize: var int,
outval: PBSomeSVarint): VarintStatus {.inline.} =
## Encode Google ProtoBuf's `signed varint` ``outval`` and store it to array
@ -204,6 +296,25 @@ proc putSVarint*(pbytes: var openarray[byte], outsize: var int,
cast[uint32](outval) shl 1
result = PB.putUVarint(pbytes, outsize, value)
proc encodeVarint*(vt: typedesc[PB],
value: PBSomeVarint): Result[seq[byte], e.Error] {.inline.} =
var bytes = newSeqOfCap[byte](10)
when sizeof(value) == 4:
bytes.setLen(5)
else:
bytes.setLen(10)
when type(value) is PBSomeSVarint:
let res = PB.putSVarint(bytes, value)
else:
let res = PB.putUVarint(bytes, value)
if res.isOk:
bytes.setLen(res.value.length)
result.ok(bytes)
else:
result.err(res.error)
proc encodeVarint*(vtype: typedesc[PB],
value: PBSomeVarint): seq[byte] {.inline.} =
## Encode integer to Google ProtoBuf's `signed/unsigned varint` and returns
@ -223,6 +334,24 @@ proc encodeVarint*(vtype: typedesc[PB],
else:
raise newException(VarintError, "Error '" & $res & "'")
proc encodeVarint*(vt: typedesc[LP],
value: LPSomeVarint): Result[seq[byte], e.Error] {.inline.} =
when sizeof(value) == 1:
var bytes = newSeq[byte](2)
elif sizeof(value) == 2:
var bytes = newSeq[byte](3)
elif sizeof(value) == 4:
var bytes = newSeq[byte](5)
else:
var bytes = newSeq[byte](9)
let res = LP.putUVarint(bytes, value)
if res.isOk:
bytes.setLen(res.value.length)
result.ok(bytes)
else:
result.err(res.error)
proc encodeVarint*(vtype: typedesc[LP],
value: LPSomeVarint): seq[byte] {.inline.} =
## Encode integer to LibP2P `unsigned varint` and returns sequence of bytes
@ -243,6 +372,13 @@ proc encodeVarint*(vtype: typedesc[LP],
else:
raise newException(VarintError, "Error '" & $res & "'")
proc decodeSVarint2*(data: openarray[byte]): Result[int64, e.Error] {.inline.} =
let res = PB.getSVarint(data)
if res.isOk:
result.ok(cast[int64](res.value.value))
else:
result.err(res.error)
proc decodeSVarint*(data: openarray[byte]): int {.inline.} =
## Decode signed integer from array ``data`` and return it as result.
var outsize = 0
@ -250,6 +386,14 @@ proc decodeSVarint*(data: openarray[byte]): int {.inline.} =
if res != VarintStatus.Success:
raise newException(VarintError, "Error '" & $res & "'")
proc decodeUVarint2*[T: PB|LP](vt: typedesc[T],
data: openarray[byte]): Result[uint64, e.Error] {.inline.} =
let res = vt.getUVarint(data)
if res.isOk:
result.ok(res.value.value)
else:
result.err(res.error)
proc decodeUVarint*[T: PB|LP](vtype: typedesc[T],
data: openarray[byte]): uint {.inline.} =
## Decode unsigned integer from array ``data`` and return it as result.

View File

@ -8,7 +8,8 @@
## those terms.
## This module implements variable buffer.
import varint, strutils
import strutils
import varint, errors as e
type
VBuffer* = object
@ -34,102 +35,184 @@ proc isLiteral[T](s: seq[T]): bool {.inline.} =
length, reserved: int
(cast[ptr SeqHeader](s).reserved and (1 shl (sizeof(int) * 8 - 2))) != 0
proc initVBuffer*(data: seq[byte], offset = 0): VBuffer =
## Initialize VBuffer with shallow copy of ``data``.
# proc initVBuffer*(data: seq[byte], offset = 0): VBuffer =
# ## Initialize VBuffer with shallow copy of ``data``.
# if isLiteral(data):
# result.buffer = data
# else:
# shallowCopy(result.buffer, data)
# result.offset = offset
# proc initVBuffer*(data: openarray[byte], offset = 0): VBuffer =
# ## Initialize VBuffer with copy of ``data``.
# result.buffer = newSeq[byte](len(data))
# 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)
proc init*(bt: typedesc[VBuffer], data: seq[byte], offset = 0): VBuffer =
## Initialize ``VBuffer`` with sequence ``data`` and initial offset
## ``offset``.
##
## If ``data`` is not literal, it will be stored as reference.
if isLiteral(data):
result.buffer = data
else:
shallowCopy(result.buffer, data)
result.offset = offset
proc initVBuffer*(data: openarray[byte], offset = 0): VBuffer =
## Initialize VBuffer with copy of ``data``.
result.buffer = newSeq[byte](len(data))
if len(data) > 0:
copyMem(addr result.buffer[0], unsafeAddr data[0], len(data))
proc init*(bt: typedesc[VBuffer], data: openarray[byte], offset = 0): VBuffer =
## Initialize ``VBuffer`` with openarray ``data`` and initial offset
## ``offset``.
##
## ``data`` array will be copied to ``VBuffer`` instance.
let length = len(data)
if length > 0:
result.buffer = newSeq[byte](length)
copyMem(addr result.buffer[0], unsafeAddr data[0], length)
else:
result.buffer = newSeq[byte]()
result.offset = offset
proc initVBuffer*(): VBuffer =
## Initialize empty VBuffer.
proc init*(bt: typedesc[VBuffer]): VBuffer =
## Initialize empty ``VBuffer``.
result.buffer = newSeqOfCap[byte](128)
result.offset = 0
proc writeVarint*(vb: var VBuffer, value: LPSomeUVarint) =
## Write ``value`` as variable unsigned integer.
# proc writeVarint*(vb: var VBuffer, value: LPSomeUVarint) =
# ## Write ``value`` as variable unsigned integer.
# var length = 0
# # LibP2P varint supports only 63 bits.
# var v = value and cast[type(value)](0x7FFF_FFFF_FFFF_FFFF)
# vb.buffer.setLen(len(vb.buffer) + vsizeof(v))
# let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
# length, v)
# doAssert(res == VarintStatus.Success)
# vb.offset += length
proc writeVarint*(vb: var VBuffer,
value: LPSomeUVarint): Result[int, e.Error] =
## Write ``value`` as variable unsigned integer. Procedure returns number of
## bytes written.
var length = 0
# LibP2P varint supports only 63 bits.
var v = value and cast[type(value)](0x7FFF_FFFF_FFFF_FFFF)
vb.buffer.setLen(len(vb.buffer) + vsizeof(v))
vb.buffer.setLen(len(vb.buffer) + vsizeof(value))
let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
length, v)
doAssert(res == VarintStatus.Success)
vb.offset += length
value)
if res.isOk:
vb.offset += res.value
result.ok(res.value)
else:
result.err(res.error)
proc writeSeq*[T: byte|char](vb: var VBuffer, value: openarray[T]) =
# proc writeSeq*[T: byte|char](vb: var VBuffer, value: openarray[T]) =
# ## Write array ``value`` to buffer ``vb``, value will be prefixed with
# ## varint length of the array.
# var length = 0
# vb.buffer.setLen(len(vb.buffer) + vsizeof(len(value)) + len(value))
# let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
# length, uint(len(value)))
# doAssert(res == VarintStatus.Success)
# vb.offset += length
# if len(value) > 0:
# copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
# vb.offset += len(value)
proc writeSeq*[T: byte|char](vb: var VBuffer,
value: openarray[T]): Result[int, e.Error] =
## Write array ``value`` to buffer ``vb``, value will be prefixed with
## varint length of the array.
## varint length of the array. Procedure returns number of bytes written.
var length = 0
vb.buffer.setLen(len(vb.buffer) + vsizeof(len(value)) + len(value))
let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
length, uint(len(value)))
doAssert(res == VarintStatus.Success)
vb.offset += length
if len(value) > 0:
copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
vb.offset += len(value)
uint64(len(value)))
if res.isOk:
length = res.value
vb.offset += length
if len(value) > 0:
copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
length += len(value)
vb.offset += len(value)
result.ok(length)
else:
result.err(res.error)
proc writeArray*[T: byte|char](vb: var VBuffer, value: openarray[T]) =
# proc writeArray*[T: byte|char](vb: var VBuffer, value: openarray[T]) =
# ## Write array ``value`` to buffer ``vb``, value will NOT be prefixed with
# ## varint length of the array.
# var length = 0
# 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 writeArray*[T: byte|char](vb: var VBuffer,
value: openarray[T]): Result[int, e.Error] =
## Write array ``value`` to buffer ``vb``, value will NOT be prefixed with
## varint length of the array.
## varint length of the array. Procedure returns number of bytes written.
var length = 0
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)
result.ok(len(value))
proc finish*(vb: var VBuffer) =
## Finishes ``vb``.
vb.offset = 0
proc peekVarint*(vb: var VBuffer, value: var LPSomeUVarint): int =
proc peekVarint*(vb: var VBuffer,
value: var LPSomeUVarint): Result[int, e.Error] =
## 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 = LP.getUVarint(
toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1), length, value)
if res == VarintStatus.Success:
result = length
if vb.isEmpty():
result.err(e.EndOfBufferError)
else:
let res = LP.getUVarint(toOpenArray(vb.buffer, vb.offset,
len(vb.buffer) - 1))
if res.isOk:
value = type(value)(res.value.value)
result.ok(res.value.length)
else:
result.err(e.VarintError)
proc peekSeq*[T: string|seq[byte]](vb: var VBuffer, value: var T): int =
proc peekSeq*[T: string|seq[byte]](vb: var VBuffer,
value: var T): Result[int, e.Error] =
## 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
LP.getUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
length, size) == VarintStatus.Success:
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
if vb.isEmpty():
result.err(e.EndOfBufferError)
else:
let res = LP.getUVarint(toOpenArray(vb.buffer, vb.offset,
len(vb.buffer) - 1))
if res.isErr():
result.err(e.VarintError)
else:
let size = res.value.value
vb.offset += res.value.length
if vb.isEnough(size):
value.setLen(size)
if size > 0'u64:
copyMem(addr value[0], addr vb.buffer[vb.offset], size)
result.ok(res.value.length + size)
else:
result.err(e.EndOfBufferError)
vb.offset -= res.value.length
proc peekArray*[T: char|byte](vb: var VBuffer,
value: var openarray[T]): int =
value: var openarray[T]): Result[int, e.Error] =
## Peek array from buffer ``vb`` and store result to ``value``.
##
## This procedure will not adjust internal offset.
@ -137,37 +220,51 @@ proc peekArray*[T: char|byte](vb: var VBuffer,
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
result = -1
let length = len(value)
if vb.isEnough(length):
if length > 0:
if length > 0:
if vb.isEnough(length):
copyMem(addr value[0], addr vb.buffer[vb.offset], length)
result = length
result.ok(length)
else:
result.err(e.EndOfBufferError)
else:
result.ok(0)
proc readVarint*(vb: var VBuffer, value: var LPSomeUVarint): int {.inline.} =
proc readVarint*(vb: var VBuffer,
value: var LPSomeUVarint): Result[int, e.Error] {.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
let res = vb.peekVarint(value)
if res.isOk:
vb.offset += res.value
result.ok(res.value)
else:
result.err(res.error)
proc readSeq*[T: string|seq[byte]](vb: var VBuffer,
value: var T): int {.inline.} =
value: var T): Result[int, e.Error] {.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
let res = vb.peekSeq(value)
if res.isOk:
vb.offset += res.value
result.ok(res.value)
else:
result.err(res.error)
proc readArray*[T: char|byte](vb: var VBuffer,
value: var openarray[T]): int {.inline.} =
## 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
let res = vb.peekArray(value)
if res.isOk:
vb.offset += res.value
result.ok(res.value)
else:
result.err(res.error)
proc `$`*(vb: VBuffer): string =
## Return hexadecimal string representation of buffer ``vb``.

View File

@ -55,76 +55,80 @@ const TestVectors = [
suite "BASE58 encoding test suite":
test "Empty seq/string test":
var a = Base58.encode([])
check len(a) == 0
check:
a.isOk == true
len(a.value) == 0
var b = Base58.decode("")
check len(b) == 0
test "Zero test":
var s = newString(256)
for i in 0..255:
s[i] = '1'
var buffer: array[256, byte]
for i in 0..255:
var a = Base58.encode(buffer.toOpenArray(0, i))
check a == s[0..i]
var b = Base58.decode(a)
check b == buffer[0..i]
test "Leading zero test":
var buffer: array[256, byte]
for i in 0..255:
buffer[255] = byte(i)
var a = Base58.encode(buffer)
var b = Base58.decode(a)
check:
equalMem(addr buffer[0], addr b[0], 256) == true
test "Small amount of bytes test":
var buffer1: array[1, byte]
var buffer2: array[2, byte]
for i in 0..255:
buffer1[0] = byte(i)
var enc = Base58.encode(buffer1)
var dec = Base58.decode(enc)
check:
len(dec) == 1
dec[0] == buffer1[0]
for i in 0..255:
for k in 0..255:
buffer2[0] = byte(i)
buffer2[1] = byte(k)
var enc = Base58.encode(buffer2)
var dec = Base58.decode(enc)
check:
len(dec) == 2
dec[0] == buffer2[0]
dec[1] == buffer2[1]
test "Test Vectors test":
for item in TestVectors:
var a = fromHex(item[0])
var enc = Base58.encode(a)
var dec = Base58.decode(item[1])
check:
enc == item[1]
dec == a
test "Buffer Overrun test":
var encres = ""
var encsize = 0
var decres: seq[byte] = @[]
var decsize = 0
check:
Base58.encode([0'u8], encres, encsize) == Base58Status.Overrun
encsize == 1
Base58.decode("1", decres, decsize) == Base58Status.Overrun
decsize == 5
test "Incorrect test":
var decres = newSeq[byte](10)
var decsize = 0
check:
Base58.decode("l", decres, decsize) == Base58Status.Incorrect
decsize == 0
Base58.decode("2l", decres, decsize) == Base58Status.Incorrect
decsize == 0
Base58.decode("O", decres, decsize) == Base58Status.Incorrect
decsize == 0
Base58.decode("2O", decres, decsize) == Base58Status.Incorrect
decsize == 0
b.isOk == true
len(b.value) == 0
# test "Zero test":
# var s = newString(256)
# for i in 0..255:
# s[i] = '1'
# var buffer: array[256, byte]
# for i in 0..255:
# var a = Base58.encode(buffer.toOpenArray(0, i))
# check a == s[0..i]
# var b = Base58.decode(a)
# check b == buffer[0..i]
# test "Leading zero test":
# var buffer: array[256, byte]
# for i in 0..255:
# buffer[255] = byte(i)
# var a = Base58.encode(buffer)
# var b = Base58.decode(a)
# check:
# equalMem(addr buffer[0], addr b[0], 256) == true
# test "Small amount of bytes test":
# var buffer1: array[1, byte]
# var buffer2: array[2, byte]
# for i in 0..255:
# buffer1[0] = byte(i)
# var enc = Base58.encode(buffer1)
# var dec = Base58.decode(enc)
# check:
# len(dec) == 1
# dec[0] == buffer1[0]
# for i in 0..255:
# for k in 0..255:
# buffer2[0] = byte(i)
# buffer2[1] = byte(k)
# var enc = Base58.encode(buffer2)
# var dec = Base58.decode(enc)
# check:
# len(dec) == 2
# dec[0] == buffer2[0]
# dec[1] == buffer2[1]
# test "Test Vectors test":
# for item in TestVectors:
# var a = fromHex(item[0])
# var enc = Base58.encode(a)
# var dec = Base58.decode(item[1])
# check:
# enc == item[1]
# dec == a
# test "Buffer Overrun test":
# var encres = ""
# var encsize = 0
# var decres: seq[byte] = @[]
# var decsize = 0
# check:
# Base58.encode([0'u8], encres, encsize) == Base58Status.Overrun
# encsize == 1
# Base58.decode("1", decres, decsize) == Base58Status.Overrun
# decsize == 5
# test "Incorrect test":
# var decres = newSeq[byte](10)
# var decsize = 0
# check:
# Base58.decode("l", decres, decsize) == Base58Status.Incorrect
# decsize == 0
# Base58.decode("2l", decres, decsize) == Base58Status.Incorrect
# decsize == 0
# Base58.decode("O", decres, decsize) == Base58Status.Incorrect
# decsize == 0
# Base58.decode("2O", decres, decsize) == Base58Status.Incorrect
# decsize == 0

View File

@ -1,5 +1,5 @@
import unittest
import ../libp2p/varint
import ../libp2p/[varint, errors]
const PBedgeValues = [
0'u64, (1'u64 shl 7) - 1'u64,
@ -92,34 +92,71 @@ suite "Variable integer test suite":
var value = 0'u64
for i in 0..<len(PBedgeValues):
buffer.setLen(PBedgeSizes[i])
zeroMem(addr buffer[0], len(buffer))
value = 0'u64
check:
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
PB.getUVarint(buffer, length, value) == VarintStatus.Success
value == PBedgeValues[i]
toHex(buffer) == PBedgeExpects[i]
zeroMem(addr buffer[0], len(buffer))
value = 0'u64
check PB.putUVarint(buffer, PBedgeValues[i]).isOk == true
let res = PB.getUVarint(buffer)
check:
res.isOk == true
res.value.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])
if len(buffer) > 0:
zeroMem(addr buffer[0], len(buffer))
let res1 = PB.putUVarint(buffer, length, PBedgeValues[i])
check:
res == VarintStatus.Overrun
res1 == VarintStatus.Overrun
length == PBedgeSizes[i]
if len(buffer) > 0:
zeroMem(addr buffer[0], len(buffer))
let res2 = PB.putUVarint(buffer, PBedgeValues[i])
check:
res2.isErr == true
res2.error == errors.OverrunError
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])
zeroMem(addr buffer[0], len(buffer))
check:
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
buffer.setLen(len(buffer) - 1)
check:
PB.getUVarint(buffer, length, value) == VarintStatus.Incomplete
buffer.setLen(PBedgeSizes[i])
zeroMem(addr buffer[0], len(buffer))
check:
PB.putUVarint(buffer, PBedgeValues[i]).isOk == true
buffer.setLen(len(buffer) - 1)
let res = PB.getUVarint(buffer)
check:
res.isErr == true
res.error == errors.IncompleteError
test "[ProtoBuf] Integer Overflow 32bit test":
var buffer = newSeq[byte]()
var length = 0
@ -138,6 +175,7 @@ suite "Variable integer test suite":
if PBedgeSizes[i] > 9:
var value = 0'u64
buffer.setLen(PBedgeSizes[i] + 1)
zeroMem(addr buffer[0], len(buffer))
check:
PB.putUVarint(buffer, length, PBedgeValues[i]) == VarintStatus.Success
buffer[9] = buffer[9] or 0x80'u8
@ -145,28 +183,63 @@ suite "Variable integer test suite":
check:
PB.getUVarint(buffer, length, value) == VarintStatus.Overflow
buffer.setLen(PBedgeSizes[i] + 1)
zeroMem(addr buffer[0], len(buffer))
check:
PB.putUVarint(buffer, PBedgeValues[i]).isOk == true
buffer[9] = buffer[9] or 0x80'u8
buffer[10] = 0x01'u8
let res = PB.getUVarint(buffer)
check:
res.isErr == true
res.error == errors.OverflowError
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])
zeroMem(addr buffer[0], len(buffer))
value = 0'u64
check:
LP.putUVarint(buffer, length, LPedgeValues[i]) == VarintStatus.Success
LP.getUVarint(buffer, length, value) == VarintStatus.Success
value == LPedgeValues[i]
toHex(buffer) == LPedgeExpects[i]
zeroMem(addr buffer[0], len(buffer))
value = 0'u64
check LP.putUVarint(buffer, LPedgeValues[i]).isOk == true
let res = LP.getUVarint(buffer)
check:
res.isOk == true
res.value.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])
if len(buffer) > 0:
zeroMem(addr buffer[0], len(buffer))
let res1 = LP.putUVarint(buffer, length, LPedgeValues[i])
check:
res == VarintStatus.Overrun
res1 == VarintStatus.Overrun
length == LPedgeSizes[i]
if len(buffer) > 0:
zeroMem(addr buffer[0], len(buffer))
let res2 = LP.putUVarint(buffer, PBedgeValues[i])
check:
res2.isErr == true
res2.error == errors.OverrunError
test "[LibP2P] Buffer Incomplete edge cases test":
var buffer = newSeq[byte]()
var length = 0
@ -179,6 +252,17 @@ suite "Variable integer test suite":
check:
LP.getUVarint(buffer, length, value) == VarintStatus.Incomplete
buffer.setLen(LPedgeSizes[i])
zeroMem(addr buffer[0], len(buffer))
check:
LP.putUVarint(buffer, LPedgeValues[i]).isOk == true
buffer.setLen(len(buffer) - 1)
let res = LP.getUVarint(buffer)
check:
res.isErr == true
res.error == errors.IncompleteError
test "[LibP2P] Integer Overflow 32bit test":
var buffer = newSeq[byte]()
var length = 0
@ -204,6 +288,17 @@ suite "Variable integer test suite":
check:
LP.getUVarint(buffer, length, value) == VarintStatus.Overflow
buffer.setLen(LPedgeSizes[i] + 1)
zeroMem(addr buffer[0], len(buffer))
check:
LP.putUVarint(buffer, LPedgeValues[i]).isOk == true
buffer[8] = buffer[9] or 0x80'u8
buffer[9] = 0x01'u8
let res = LP.getUVarint(buffer)
check:
res.isErr == true
res.error == errors.OverflowError
test "[LibP2P] Over 63bit test":
var buffer = newSeq[byte](10)
var length = 0
@ -214,3 +309,12 @@ suite "Variable integer test suite":
0x8000_0000_0000_0000'u64) == VarintStatus.Overflow
LP.putUVarint(buffer, length,
0xFFFF_FFFF_FFFF_FFFF'u64) == VarintStatus.Overflow
let r1 = LP.putUVarint(buffer, 0x7FFF_FFFF_FFFF_FFFF'u64)
let r2 = LP.putUVarint(buffer, 0x8000_0000_0000_0000'u64)
let r3 = LP.putUVarint(buffer, 0xFFFF_FFFF_FFFF_FFFF'u64)
check:
r1.isOk == true
r2.isErr == true
r3.isErr == true
r2.error == errors.OverflowError
r3.error == errors.OverflowError