From 9a7fca48bb0412631ceeb9911ec6b9a36f3c2987 Mon Sep 17 00:00:00 2001 From: MechSlayer <0jcrespo1996@gmail.com> Date: Thu, 11 Sep 2025 05:02:22 +0200 Subject: [PATCH] feature: implement UUID generation and conversion functions for Linux and Windows --- Source/DrangPlatform/CMakeLists.txt | 6 + Source/DrangPlatform/Source/linux/uuid.c | 20 ++++ Source/DrangPlatform/Source/uuid.c | 142 +++++++++++++++++++++++ Source/DrangPlatform/Source/win32/uuid.c | 23 ++++ 4 files changed, 191 insertions(+) create mode 100644 Source/DrangPlatform/Source/linux/uuid.c create mode 100644 Source/DrangPlatform/Source/uuid.c create mode 100644 Source/DrangPlatform/Source/win32/uuid.c diff --git a/Source/DrangPlatform/CMakeLists.txt b/Source/DrangPlatform/CMakeLists.txt index cfc2004..e22e8e6 100644 --- a/Source/DrangPlatform/CMakeLists.txt +++ b/Source/DrangPlatform/CMakeLists.txt @@ -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 () diff --git a/Source/DrangPlatform/Source/linux/uuid.c b/Source/DrangPlatform/Source/linux/uuid.c new file mode 100644 index 0000000..2e4e057 --- /dev/null +++ b/Source/DrangPlatform/Source/linux/uuid.c @@ -0,0 +1,20 @@ +#include "drang/error.h" +#include +#include +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; +} \ No newline at end of file diff --git a/Source/DrangPlatform/Source/uuid.c b/Source/DrangPlatform/Source/uuid.c new file mode 100644 index 0000000..7f6ba5d --- /dev/null +++ b/Source/DrangPlatform/Source/uuid.c @@ -0,0 +1,142 @@ +#include "drang/error.h" +#include +#include +#include +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() +} diff --git a/Source/DrangPlatform/Source/win32/uuid.c b/Source/DrangPlatform/Source/win32/uuid.c new file mode 100644 index 0000000..9365ab6 --- /dev/null +++ b/Source/DrangPlatform/Source/win32/uuid.c @@ -0,0 +1,23 @@ +#include "common.h" +#include "drang/error.h" +#include +#include +#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; +}