Add hex encode/decode helpers

This commit is contained in:
Carl Niklas Rydberg 2025-12-20 00:05:17 +01:00
parent b32c164313
commit 428f657f4f
3 changed files with 169 additions and 0 deletions

View file

@ -56,6 +56,7 @@ set(AMDUAT_UTIL_SRCS
src/internal/arena.c src/internal/arena.c
src/internal/varint.c src/internal/varint.c
src/internal/endian.c src/internal/endian.c
src/internal/hex.c
) )
set(AMDUAT_ASL_SRCS set(AMDUAT_ASL_SRCS

34
include/amduat/util/hex.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef AMDUAT_UTIL_HEX_H
#define AMDUAT_UTIL_HEX_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
size_t amduat_hex_encoded_len(size_t byte_len); /* returns byte_len * 2 */
size_t amduat_hex_encoded_size(size_t byte_len); /* returns encoded_len + 1 for NUL */
bool amduat_hex_encode_lower(const uint8_t *bytes,
size_t byte_len,
char *out,
size_t out_len);
/* Decodes exactly; rejects odd length or invalid hex. Accept upper/lowercase. */
bool amduat_hex_decode(const char *hex,
uint8_t *out,
size_t out_len);
/* Convenience: allocates output; caller frees with free(). */
bool amduat_hex_decode_alloc(const char *hex,
uint8_t **out,
size_t *out_len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_UTIL_HEX_H */

134
src/internal/hex.c Normal file
View file

@ -0,0 +1,134 @@
#include "amduat/util/hex.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
size_t amduat_hex_encoded_len(size_t byte_len) {
if (byte_len > (SIZE_MAX / 2u)) {
return 0;
}
return byte_len * 2u;
}
size_t amduat_hex_encoded_size(size_t byte_len) {
size_t encoded_len = amduat_hex_encoded_len(byte_len);
if (encoded_len == 0 && byte_len != 0) {
return 0;
}
if (encoded_len > (SIZE_MAX - 1u)) {
return 0;
}
return encoded_len + 1u;
}
bool amduat_hex_encode_lower(const uint8_t *bytes,
size_t byte_len,
char *out,
size_t out_len) {
static const char kHexLower[] = "0123456789abcdef";
size_t encoded_len = amduat_hex_encoded_len(byte_len);
size_t encoded_size = amduat_hex_encoded_size(byte_len);
if (encoded_len == 0 && byte_len != 0) {
return false;
}
if (encoded_size == 0 || out == NULL || out_len < encoded_size) {
return false;
}
if (byte_len > 0 && bytes == NULL) {
return false;
}
for (size_t i = 0; i < byte_len; ++i) {
uint8_t byte = bytes[i];
out[i * 2u] = kHexLower[(byte >> 4u) & 0x0fu];
out[i * 2u + 1u] = kHexLower[byte & 0x0fu];
}
out[encoded_len] = '\0';
return true;
}
static bool amduat_hex_nibble(char c, uint8_t *out) {
if (c >= '0' && c <= '9') {
*out = (uint8_t)(c - '0');
return true;
}
if (c >= 'a' && c <= 'f') {
*out = (uint8_t)(c - 'a' + 10);
return true;
}
if (c >= 'A' && c <= 'F') {
*out = (uint8_t)(c - 'A' + 10);
return true;
}
return false;
}
bool amduat_hex_decode(const char *hex, uint8_t *out, size_t out_len) {
if (hex == NULL) {
return false;
}
if (out_len > 0 && out == NULL) {
return false;
}
size_t hex_len = strlen(hex);
if ((hex_len & 1u) != 0u) {
return false;
}
size_t expected_len = hex_len / 2u;
if (expected_len != out_len) {
return false;
}
for (size_t i = 0; i < out_len; ++i) {
uint8_t hi = 0;
uint8_t lo = 0;
if (!amduat_hex_nibble(hex[i * 2u], &hi) ||
!amduat_hex_nibble(hex[i * 2u + 1u], &lo)) {
return false;
}
out[i] = (uint8_t)((hi << 4u) | lo);
}
return true;
}
bool amduat_hex_decode_alloc(const char *hex, uint8_t **out, size_t *out_len) {
if (out != NULL) {
*out = NULL;
}
if (out_len != NULL) {
*out_len = 0;
}
if (hex == NULL || out == NULL || out_len == NULL) {
return false;
}
size_t hex_len = strlen(hex);
if ((hex_len & 1u) != 0u) {
return false;
}
size_t bytes_len = hex_len / 2u;
if (bytes_len == 0) {
return true;
}
uint8_t *buf = (uint8_t *)malloc(bytes_len);
if (buf == NULL) {
return false;
}
if (!amduat_hex_decode(hex, buf, bytes_len)) {
free(buf);
return false;
}
*out = buf;
*out_len = bytes_len;
return true;
}