byteutils: allow empty output buffers (#157)

* byteutils: allow empty output buffers

* er, closed
This commit is contained in:
Jacek Sieka 2023-01-19 08:19:26 +01:00 committed by GitHub
parent ac602b5086
commit 32d6f43fff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 40 deletions

View File

@ -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]

View File

@ -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"