expose hex_decode APIs

Functions hex_decode_init(), hex_decode_char() and hex_decode_finish()
are now exposed, as well as the context hex_decode_ctx_t. They now are
respectively called isc_hex_decodeinit(), isc_hex_decodechar(),
isc_hex_decodefinish() and isc_hex_decodectx_t.

This enable to re-implement the functionality of isc_hex_decodestring()
in contextes where the input is not a NULL-terminated string, but, for
example, individual characters extracted (and avoid creating an
intermediate buffer to store them). This also enable to decode a stream
of hex characters where only hex characters are expected (i.e. no white
spaces).
This commit is contained in:
Colin Vidal 2025-05-16 18:33:13 +02:00
parent ac4cf4cce8
commit e34dd2b73e
2 changed files with 72 additions and 24 deletions

View file

@ -83,25 +83,15 @@ isc_hex_totext(isc_region_t *source, int wordlength, const char *wordbreak,
return ISC_R_SUCCESS;
}
/*%
* State of a hex decoding process in progress.
*/
typedef struct {
int length; /*%< Desired length of binary data or -1 */
isc_buffer_t *target; /*%< Buffer for resulting binary data */
int digits; /*%< Number of buffered hex digits */
int val[2];
} hex_decode_ctx_t;
static void
hex_decode_init(hex_decode_ctx_t *ctx, int length, isc_buffer_t *target) {
void
isc_hex_decodeinit(isc_hex_decodectx_t *ctx, int length, isc_buffer_t *target) {
ctx->digits = 0;
ctx->length = length;
ctx->target = target;
}
static isc_result_t
hex_decode_char(hex_decode_ctx_t *ctx, int c) {
isc_result_t
isc_hex_decodechar(isc_hex_decodectx_t *ctx, int c) {
uint8_t hexval;
hexval = isc_hex_char(c);
@ -126,8 +116,8 @@ hex_decode_char(hex_decode_ctx_t *ctx, int c) {
return ISC_R_SUCCESS;
}
static isc_result_t
hex_decode_finish(hex_decode_ctx_t *ctx) {
isc_result_t
isc_hex_decodefinish(isc_hex_decodectx_t *ctx) {
if (ctx->length > 0) {
return ISC_R_UNEXPECTEDEND;
}
@ -140,14 +130,14 @@ hex_decode_finish(hex_decode_ctx_t *ctx) {
isc_result_t
isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
unsigned int before, after;
hex_decode_ctx_t ctx;
isc_hex_decodectx_t ctx;
isc_textregion_t *tr;
isc_token_t token;
bool eol;
REQUIRE(length >= -2);
hex_decode_init(&ctx, length, target);
isc_hex_decodeinit(&ctx, length, target);
before = isc_buffer_usedlength(target);
while (ctx.length != 0) {
@ -165,14 +155,14 @@ isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
}
tr = &token.value.as_textregion;
for (i = 0; i < tr->length; i++) {
RETERR(hex_decode_char(&ctx, tr->base[i]));
RETERR(isc_hex_decodechar(&ctx, tr->base[i]));
}
}
after = isc_buffer_usedlength(target);
if (ctx.length < 0) {
isc_lex_ungettoken(lexer, &token);
}
RETERR(hex_decode_finish(&ctx));
RETERR(isc_hex_decodefinish(&ctx));
if (length == -2 && before == after) {
return ISC_R_UNEXPECTEDEND;
}
@ -181,9 +171,9 @@ isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
isc_result_t
isc_hex_decodestring(const char *cstr, isc_buffer_t *target) {
hex_decode_ctx_t ctx;
isc_hex_decodectx_t ctx;
hex_decode_init(&ctx, -1, target);
isc_hex_decodeinit(&ctx, -1, target);
for (;;) {
int c = *cstr++;
if (c == '\0') {
@ -192,9 +182,9 @@ isc_hex_decodestring(const char *cstr, isc_buffer_t *target) {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
continue;
}
RETERR(hex_decode_char(&ctx, c));
RETERR(isc_hex_decodechar(&ctx, c));
}
RETERR(hex_decode_finish(&ctx));
RETERR(isc_hex_decodefinish(&ctx));
return ISC_R_SUCCESS;
}

View file

@ -17,6 +17,16 @@
#include <isc/types.h>
/*%
* State of a hex decoding process in progress.
*/
typedef struct {
int length; /*%< Desired length of binary data or -1 */
isc_buffer_t *target; /*%< Buffer for resulting binary data */
int digits; /*%< Number of buffered hex digits */
int val[2];
} isc_hex_decodectx_t;
/*
* An `isc__hex_char` table entry is non-zero if the character is a hex digit;
* You can subtract the table entry from the character to convert the hex digit
@ -58,6 +68,54 @@ isc_hex_totext(isc_region_t *source, int wordlength, const char *wordbreak,
* necessary.
*/
/*
* The 3 following functions are internally used and wrapped by
* `isc_hex_decodestring()`, which can be directly used for simpler cases.
* However, for more complex cases (or cases which, for instance, must not have
* white spaces, or if the input is not a null-terminated string) using those
* lower-level API might be usefull.
*/
void
isc_hex_decodeinit(isc_hex_decodectx_t *ctx, int length, isc_buffer_t *target);
/*!<
* \brief Initialize the hex decoder context
*
* Requires:
*\li 'ctx' is non-null.
*\li 'length' is the number of bytes that will have to be decoded
*\li 'target' is the buffer which the decoded hex chars will be written to.
*/
isc_result_t
isc_hex_decodechar(isc_hex_decodectx_t *ctx, int c);
/*!<
* \brief Decode an individual hex character
*
* Requires:
*\li 'ctx' is non-null.
*\li 'c' is the hexadecimal character to decode
*
* Returns:
* \li #ISC_R_BADHEX -- 'c' is not an hexadecimal char
* \li #ISC_R_SUCCESS -- 'c' is decoded
*/
isc_result_t
isc_hex_decodefinish(isc_hex_decodectx_t *ctx);
/*!<
* \brief Verifies that all the decoded characters used the expected length
* passed to `hex_decode_init()`
*
* Requires:
*\li 'ctx' is non-null.
*
* Returns:
* \li #ISC_R_UNEXPECTEDEND -- less bytes than expected has been decoded
* \li #ISC_R_BADHEX -- last decoded character is not an hexadecimal one
* \li #ISC_R_SUCCESS -- all the bytes are decoded as expected
*/
isc_result_t
isc_hex_decodestring(const char *cstr, isc_buffer_t *target);
/*!<