amduat/src/internal/hex.c

165 lines
3.4 KiB
C

#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;
}
bool amduat_hex_encode_alloc(const uint8_t *bytes,
size_t byte_len,
char **out) {
size_t out_len;
char *buf;
if (out != NULL) {
*out = NULL;
}
if (out == NULL) {
return false;
}
out_len = amduat_hex_encoded_size(byte_len);
if (out_len == 0) {
return byte_len == 0;
}
buf = (char *)malloc(out_len);
if (buf == NULL) {
return false;
}
if (!amduat_hex_encode_lower(bytes, byte_len, buf, out_len)) {
free(buf);
return false;
}
*out = buf;
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;
}