byteutils: allow empty output buffers (#157)
* byteutils: allow empty output buffers * er, closed
This commit is contained in:
parent
ac602b5086
commit
32d6f43fff
|
@ -40,77 +40,122 @@ template skip0xPrefix(hexStr: openArray[char]): int =
|
|||
if hexStr.len > 1 and hexStr[0] == '0' and hexStr[1] in {'x', 'X'}: 2
|
||||
else: 0
|
||||
|
||||
func hexToByteArrayImpl(hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int): int
|
||||
{.raises: [ValueError, Defect].} =
|
||||
## Read a hex string and store it in a byte array `output`. No "endianness" reordering is done.
|
||||
## Allows specifying the byte range to process into the array
|
||||
func hexToByteArrayImpl(
|
||||
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int):
|
||||
int {.raises: [ValueError, Defect].} =
|
||||
var sIdx = skip0xPrefix(hexStr)
|
||||
# Fun with closed intervals
|
||||
doAssert fromIdx >= 0 and
|
||||
toIdx <= output.high and
|
||||
fromIdx <= (toIdx + 1)
|
||||
|
||||
doAssert(fromIdx >= 0 and toIdx >= fromIdx and fromIdx < output.len and toIdx < output.len)
|
||||
let sz = toIdx - fromIdx + 1
|
||||
let sz = toIdx + 1 - fromIdx
|
||||
|
||||
if hexStr.len - sIdx < 2*sz:
|
||||
raise (ref ValueError)(msg: "hex string too short")
|
||||
|
||||
sIdx += fromIdx * 2
|
||||
for bIdx in fromIdx ..< sz + fromIdx:
|
||||
output[bIdx] = hexStr[sIdx].readHexChar shl 4 or hexStr[sIdx + 1].readHexChar
|
||||
output[bIdx] =
|
||||
(hexStr[sIdx].readHexChar shl 4) or
|
||||
hexStr[sIdx + 1].readHexChar
|
||||
inc(sIdx, 2)
|
||||
|
||||
sIdx
|
||||
|
||||
func hexToByteArray*(hexStr: string, output: var openArray[byte], fromIdx, toIdx: int)
|
||||
{.raises: [ValueError, Defect].} =
|
||||
## Read a hex string and store it in a byte array `output`. No "endianness" reordering is done.
|
||||
## Allows specifying the byte range to process into the array.
|
||||
## The hex input may be longer than strictly necessary.
|
||||
func hexToByteArray*(
|
||||
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int)
|
||||
{.raises: [ValueError, Defect].} =
|
||||
## Read hex-encoded data from `hexStr[mapHex(fromIdx..toIdx)]` and store
|
||||
## corresponding bytes in `output[fromIdx..toIdx]` where `mapHex` takes into
|
||||
## account stripped characters.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short or contains invalid
|
||||
## data in the parsed part
|
||||
## * Longer strings are allowed
|
||||
## * No "endianness" reordering is done
|
||||
## * Allows specifying the byte range to process into the array - the indices
|
||||
## are mapped to the string after potentially stripping "0x"
|
||||
discard hexToByteArrayImpl(hexStr, output, fromIdx, toIdx)
|
||||
|
||||
func hexToByteArray*(hexStr: string, output: var openArray[byte])
|
||||
{.raises: [ValueError, Defect], inline.} =
|
||||
## Read a hex string and store it in a byte array `output`. No "endianness" reordering is done.
|
||||
## The hex input may be longer than strictly necessary.
|
||||
func hexToByteArray*(hexStr: openArray[char], output: var openArray[byte])
|
||||
{.raises: [ValueError, Defect].} =
|
||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||
## `output`.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short or contains invalid
|
||||
## data
|
||||
## * Longer strings are allowed
|
||||
## * No "endianness" reordering is done
|
||||
hexToByteArray(hexStr, output, 0, output.high)
|
||||
|
||||
func hexToByteArray*[N: static[int]](hexStr: string): array[N, byte]
|
||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
||||
## Read an hex string and store it in a byte array. No "endianness" reordering is done.
|
||||
## The hex input may be longer than strictly necessary.
|
||||
func hexToByteArray*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
||||
{.raises: [ValueError, Defect], noinit.}=
|
||||
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short or contains invalid
|
||||
## data
|
||||
## * Longer strings are allowed
|
||||
## * No "endianness" reordering is done
|
||||
hexToByteArray(hexStr, result)
|
||||
|
||||
func hexToByteArray*(hexStr: string, N: static int): array[N, byte]
|
||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
||||
## Read an hex string and store it in a byte array. No "endianness" reordering is done.
|
||||
## The hex input may be longer than strictly necessary.
|
||||
func hexToByteArray*(hexStr: openArray[char], N: static int): array[N, byte]
|
||||
{.raises: [ValueError, Defect], noinit.}=
|
||||
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short or contains invalid
|
||||
## data
|
||||
## * Longer strings are allowed
|
||||
## * No "endianness" reordering is done
|
||||
hexToByteArray(hexStr, result)
|
||||
|
||||
func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int)
|
||||
{.raises: [ValueError, Defect].} =
|
||||
## Read a hex string and store it in a byte array `output`. No "endianness" reordering is done.
|
||||
## Allows specifying the byte range to process into the array. The entire input must be consumed.
|
||||
if hexToByteArrayImpl(hexStr, output, fromIdx, toIdx) != hexStr.len:
|
||||
raise (ref ValueError)(msg: "hex string too long")
|
||||
|
||||
func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte])
|
||||
{.raises: [ValueError, Defect], inline.} =
|
||||
## Read a hex string and store it in a byte array `output`. No "endianness" reordering is done.
|
||||
## The entire input must be consumed.
|
||||
hexToByteArrayStrict(hexStr, output, 0, output.high)
|
||||
{.raises: [ValueError, Defect].} =
|
||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||
## `output`.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short, too long or contains
|
||||
## invalid data
|
||||
## * No "endianness" reordering is done
|
||||
if hexToByteArrayImpl(hexStr, output, 0, output.high) != hexStr.len:
|
||||
raise (ref ValueError)(msg: "hex string too long")
|
||||
|
||||
func hexToByteArrayStrict*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
||||
## Read an hex string and store it in a byte array. No "endianness" reordering is done.
|
||||
## The entire input must be consumed.
|
||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||
## `output`.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short, too long or contains
|
||||
## invalid data
|
||||
## * No "endianness" reordering is done
|
||||
hexToByteArrayStrict(hexStr, result)
|
||||
|
||||
func hexToByteArrayStrict*(hexStr: openArray[char], N: static int): array[N, byte]
|
||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
||||
## Read an hex string and store it in a byte array. No "endianness" reordering is done.
|
||||
## The entire input must be consumed.
|
||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||
## `output`.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short, too long or contains
|
||||
## invalid data
|
||||
## * No "endianness" reordering is done
|
||||
hexToByteArrayStrict(hexStr, result)
|
||||
|
||||
func fromHex*[N](A: type array[N, byte], hexStr: string): A
|
||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
||||
## Read an hex string and store it in a byte array. No "endianness" reordering is done.
|
||||
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
## * `ValueError` is raised if the string is too short or contains invalid
|
||||
## data
|
||||
## * Longer strings are allowed
|
||||
## * No "endianness" reordering is done
|
||||
hexToByteArray(hexStr, result)
|
||||
|
||||
func hexToPaddedByteArray*[N: static[int]](hexStr: string): array[N, byte]
|
||||
|
|
|
@ -65,6 +65,33 @@ suite "Byte utils":
|
|||
expect(ValueError): discard hexToByteArray[1]("")
|
||||
expect(ValueError): discard hexToByteArray[1]("1")
|
||||
|
||||
test "hexToByteArray: missing bytes":
|
||||
var buffer: array[1, byte]
|
||||
expect(ValueError):
|
||||
hexToByteArray("0x", buffer)
|
||||
expect(ValueError):
|
||||
hexToByteArray("", buffer)
|
||||
expect(ValueError):
|
||||
hexToByteArray("0", buffer)
|
||||
|
||||
test "valid hex with empty array":
|
||||
var buffer: seq[byte]
|
||||
hexToByteArray("0x123", openArray[byte](buffer))
|
||||
check(buffer == seq[byte](@[]))
|
||||
|
||||
test "valid hex with empty array of size":
|
||||
var buffer: seq[byte] = newSeq[byte](4)
|
||||
hexToByteArray("00000123", openArray[byte](buffer))
|
||||
check(buffer == @[0.byte, 0.byte, 1.byte, 35.byte])
|
||||
check buffer.toHex == "00000123"
|
||||
|
||||
test "empty output array is ok":
|
||||
var output: array[0, byte]
|
||||
|
||||
hexToByteArray("", output)
|
||||
hexToByteArray("0x", output)
|
||||
hexToByteArray("0x32", output)
|
||||
|
||||
test "array.fromHex":
|
||||
let
|
||||
s = "0x12345678"
|
||||
|
|
Loading…
Reference in New Issue