feature: implement UUID generation and conversion functions for Linux and Windows

This commit is contained in:
MechSlayer 2025-09-11 05:02:22 +02:00
parent 4917595535
commit 9a7fca48bb
4 changed files with 191 additions and 0 deletions

View file

@ -62,6 +62,12 @@ endif ()
if (UNIX)
target_compile_definitions(${MODULE_NAME} PRIVATE _GNU_SOURCE=1)
find_library(UUID_LIB uuid NAMES libuuid)
if (NOT UUID_LIB)
target_compile_definitions(${MODULE_NAME} PRIVATE DRANG_NO_LIBUUID=1)
else ()
target_link_libraries(${MODULE_NAME} PRIVATE ${UUID_LIB})
endif ()
endif ()

View file

@ -0,0 +1,20 @@
#include "drang/error.h"
#include <drang/uuid.h>
#include <uuid/uuid.h>
int drang_uuid_generate(drang_uuid_t *out_uuid)
{
uuid_t linux_uuid;
uuid_generate_random(linux_uuid);
// Convert to our format (UUID is stored as big-endian bytes)
out_uuid->high = ((uint64_t)linux_uuid[0] << 56) | ((uint64_t)linux_uuid[1] << 48) |
((uint64_t)linux_uuid[2] << 40) | ((uint64_t)linux_uuid[3] << 32) |
((uint64_t)linux_uuid[4] << 24) | ((uint64_t)linux_uuid[5] << 16) |
((uint64_t)linux_uuid[6] << 8) | ((uint64_t)linux_uuid[7]);
out_uuid->low = ((uint64_t)linux_uuid[8] << 56) | ((uint64_t)linux_uuid[9] << 48) |
((uint64_t)linux_uuid[10] << 40) | ((uint64_t)linux_uuid[11] << 32) |
((uint64_t)linux_uuid[12] << 24) | ((uint64_t)linux_uuid[13] << 16) |
((uint64_t)linux_uuid[14] << 8) | ((uint64_t)linux_uuid[15]);
return DRANG_EOK;
}

View file

@ -0,0 +1,142 @@
#include "drang/error.h"
#include <ctype.h>
#include <drang/uuid.h>
#include <stdio.h>
int drang_uuid_to_string(const drang_uuid_t *uuid, char *out_str, size_t str_size)
{
uint8_t bytes[16];
// Convert back to bytes for string formatting
for (int i = 0; i < 8; i++) {
bytes[i] = (uint8_t)(uuid->high >> (56 - i * 8));
bytes[i + 8] = (uint8_t)(uuid->low >> (56 - i * 8));
}
return snprintf(out_str, str_size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11],
bytes[12], bytes[13], bytes[14], bytes[15]);
}
static int hex_char_to_value(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static int parse_hex_byte(const char *str, uint8_t *byte) {
int high = hex_char_to_value(str[0]);
int low = hex_char_to_value(str[1]);
if (high < 0 || low < 0) {
return -1;
}
*byte = (uint8_t)((high << 4) | low);
return 0;
}
int drang_uuid_from_string(const char *str, size_t str_size, drang_uuid_t *out_uuid)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(str, DRANG_EINVAL);
DRANG_CHECK(out_uuid, DRANG_EINVAL);
// Work with a copy to handle different formats
char normalized[37] = {0}; // 36 chars + null terminator
const char *src = str;
char *dst = normalized;
int hex_count = 0;
// Skip leading brace if present
if (*src == '{') {
src++;
}
// Copy only hex digits and hyphens, convert to lowercase
while (*src && dst < normalized + 36) {
if (isxdigit(*src)) {
*dst++ = (char)tolower(*src);
hex_count++;
} else if (*src == '-') {
*dst++ = '-';
} else if (*src == '}' && *(src + 1) == '\0') {
// Allow trailing brace
break;
} else if (!isspace(*src)) {
// Invalid character (ignoring whitespace)
DRANG_FAIL(DRANG_EINVAL);
}
src++;
}
// Must have exactly 32 hex digits
DRANG_CHECK(hex_count == 32, DRANG_EINVAL);
// Determine format and validate
const int len = (int)(dst - normalized);
int has_hyphens = 0;
if (len == 36) {
// Standard format with hyphens: 8-4-4-4-12
if (normalized[8] != '-' || normalized[13] != '-' ||
normalized[18] != '-' || normalized[23] != '-') {
DRANG_FAIL(DRANG_EINVAL);
}
has_hyphens = 1;
} else if (len == 32) {
// No hyphens format
has_hyphens = 0;
} else {
DRANG_FAIL(DRANG_EINVAL);
}
// Parse the hex digits into bytes
uint8_t bytes[16];
const char *hex_positions[16];
if (has_hyphens) {
// Standard format positions
hex_positions[0] = &normalized[0]; // 8 chars: time_low
hex_positions[1] = &normalized[2];
hex_positions[2] = &normalized[4];
hex_positions[3] = &normalized[6];
hex_positions[4] = &normalized[9]; // 4 chars: time_mid
hex_positions[5] = &normalized[11];
hex_positions[6] = &normalized[14]; // 4 chars: time_hi_and_version
hex_positions[7] = &normalized[16];
hex_positions[8] = &normalized[19]; // 4 chars: clock_seq_and_reserved + clock_seq_low
hex_positions[9] = &normalized[21];
hex_positions[10] = &normalized[24]; // 12 chars: node
hex_positions[11] = &normalized[26];
hex_positions[12] = &normalized[28];
hex_positions[13] = &normalized[30];
hex_positions[14] = &normalized[32];
hex_positions[15] = &normalized[34];
} else {
// No hyphens format
for (int i = 0; i < 16; i++) {
hex_positions[i] = &normalized[i * 2];
}
}
// Convert hex pairs to bytes
for (int i = 0; i < 16; i++) {
DRANG_CHECK(parse_hex_byte(hex_positions[i], &bytes[i]) == 0, DRANG_EINVAL);
}
// Convert bytes to uint64_t values
out_uuid->high = ((uint64_t)bytes[0] << 56) | ((uint64_t)bytes[1] << 48) |
((uint64_t)bytes[2] << 40) | ((uint64_t)bytes[3] << 32) |
((uint64_t)bytes[4] << 24) | ((uint64_t)bytes[5] << 16) |
((uint64_t)bytes[6] << 8) | ((uint64_t)bytes[7]);
out_uuid->low = ((uint64_t)bytes[8] << 56) | ((uint64_t)bytes[9] << 48) |
((uint64_t)bytes[10] << 40) | ((uint64_t)bytes[11] << 32) |
((uint64_t)bytes[12] << 24) | ((uint64_t)bytes[13] << 16) |
((uint64_t)bytes[14] << 8) | ((uint64_t)bytes[15]);
DRANG_END_TRY_IGNORE()
}

View file

@ -0,0 +1,23 @@
#include "common.h"
#include "drang/error.h"
#include <drang/uuid.h>
#include <rpc.h>
#pragma comment(lib, "rpcrt4.lib")
int drang_uuid_generate(drang_uuid_t *out_uuid)
{
UUID win_uuid = {0};
const RPC_STATUS status = UuidCreate(&win_uuid);
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
return DRANG_EPLATFORM;
}
// Convert Windows UUID to our format
// Windows UUID is stored in mixed-endian format
out_uuid->high = ((uint64_t)win_uuid.Data1 << 32) | ((uint64_t)win_uuid.Data2 << 16) | win_uuid.Data3;
out_uuid->low = 0;
for (int i = 0; i < 8; ++i) {
out_uuid->low = (out_uuid->low << 8) | win_uuid.Data4[i];
}
return DRANG_EOK;
}