Add hex encode/decode helpers
This commit is contained in:
parent
b32c164313
commit
428f657f4f
|
|
@ -56,6 +56,7 @@ set(AMDUAT_UTIL_SRCS
|
|||
src/internal/arena.c
|
||||
src/internal/varint.c
|
||||
src/internal/endian.c
|
||||
src/internal/hex.c
|
||||
)
|
||||
|
||||
set(AMDUAT_ASL_SRCS
|
||||
|
|
|
|||
34
include/amduat/util/hex.h
Normal file
34
include/amduat/util/hex.h
Normal 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
134
src/internal/hex.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in a new issue