mie: add a namespace/uuid-based map data structure

rather than a traditional dictionary or hashmap, this data structure supports a one-to-one
mapping between a UUID and a value. this is an intrusive data structure like mie_name_map
(a value struct must include an instance of mie_id), but this one does not support name collisions.

mie_id_map generates and issues UUIDs based on a name provided by the caller. All UUIDs are v5,
meaning they are generated using a SHA1 hash of a namespace UUID (specified when the mie_id_map
is initialised), and a unique name provided by the caller.

mie_id can also be used standalone to generate, store, and stringify UUIDs.
This commit is contained in:
2026-01-04 14:03:12 +00:00
parent 915cf836f2
commit 21bcbb7edc
2 changed files with 211 additions and 0 deletions

151
mie/id.c Normal file
View File

@@ -0,0 +1,151 @@
#include <blue/core/bstr.h>
#include <blue/core/endian.h>
#include <blue/core/hash.h>
#include <blue/core/random.h>
#include <mie/id.h>
#include <string.h>
static inline int id_memcmp(const uint8_t *a, const uint8_t *b)
{
return memcmp(a, b, MIE_ID_NR_BYTES);
}
static mie_id *get_id(const b_btree *tree, mie_id *key)
{
b_btree_node *cur = tree->b_root;
while (cur) {
mie_id *cur_node = b_unbox(mie_id, cur, e_node);
int cmp = memcmp(
key->id_bytes, cur_node->id_bytes, sizeof key->id_bytes);
if (cmp > 0) {
cur = b_btree_right(cur);
} else if (cmp < 0) {
cur = b_btree_left(cur);
} else {
return cur_node;
}
}
return NULL;
}
void put_id(b_btree *tree, mie_id *node)
{
if (!tree->b_root) {
tree->b_root = &node->e_node;
b_btree_insert_fixup(tree, &node->e_node);
return;
}
b_btree_node *cur = tree->b_root;
while (1) {
mie_id *cur_node = b_unbox(mie_id, cur, e_node);
b_btree_node *next = NULL;
int cmp = memcmp(
node->id_bytes, cur_node->id_bytes, sizeof node->id_bytes);
if (cmp > 0) {
next = b_btree_right(cur);
if (!next) {
b_btree_put_right(cur, &node->e_node);
break;
}
} else if (cmp < 0) {
next = b_btree_left(cur);
if (!next) {
b_btree_put_left(cur, &node->e_node);
break;
}
} else {
return;
}
cur = next;
}
b_btree_insert_fixup(tree, &node->e_node);
}
void mie_id_init(
mie_id *out, uint32_t a, uint16_t b, uint16_t c, uint16_t d, uint64_t e)
{
b_i32 x_a = b_i32_htob(a);
b_i16 x_b = b_i16_htob(b);
b_i16 x_c = b_i16_htob(c);
b_i16 x_d = b_i16_htob(d);
b_i64 x_e = b_i64_htob(e);
memcpy(&out->id_bytes[0], x_a.i_bytes, sizeof x_a.i_bytes);
memcpy(&out->id_bytes[4], x_b.i_bytes, sizeof x_b.i_bytes);
memcpy(&out->id_bytes[6], x_c.i_bytes, sizeof x_c.i_bytes);
memcpy(&out->id_bytes[8], x_d.i_bytes, sizeof x_d.i_bytes);
memcpy(&out->id_bytes[10], &x_e.i_bytes[2], sizeof x_e.i_bytes - 2);
}
void mie_id_init_zero(mie_id *out)
{
memset(out, 0x0, sizeof *out);
}
void mie_id_init_random(mie_id *out)
{
b_random_ctx *random = b_random_global_ctx();
b_random_next_bytes(
random, (unsigned char *)out->id_bytes, sizeof out->id_bytes);
}
void mie_id_init_ns(mie_id *out, const mie_id *ns, const b_rope *name)
{
b_hash_ctx hash;
b_hash_ctx_init(&hash, B_HASH_SHA1);
b_hash_ctx_update(&hash, ns->id_bytes, sizeof ns->id_bytes);
b_hash_ctx_update_rope(&hash, name);
b_hash_ctx_finish(&hash, out->id_bytes, sizeof out->id_bytes);
out->id_bytes[6] &= 0x0F;
out->id_bytes[6] |= 0x50;
out->id_bytes[8] &= 0x3F;
out->id_bytes[8] |= 0x80;
}
void mie_id_to_string(const mie_id *id, char *out, size_t max)
{
b_bstr str;
b_bstr_begin(&str, out, max);
for (size_t i = 0; i < sizeof id->id_bytes; i++) {
if (i == 4 || i == 6 || i == 8 || i == 10) {
b_bstr_write_char(&str, '-');
}
b_bstr_write_fmt(&str, "%02x", id->id_bytes[i]);
}
}
void mie_id_map_init(struct mie_id_map *map, const mie_id *ns)
{
memset(map, 0x0, sizeof *map);
map->map_ns_id = *ns;
}
const mie_id *mie_id_map_get_ns(const struct mie_id_map *map)
{
return &map->map_ns_id;
}
void mie_id_map_put(struct mie_id_map *map, mie_id *id, const b_rope *name)
{
mie_id_init_ns(id, &map->map_ns_id, name);
put_id(&map->map_entries, id);
}
mie_id *mie_id_map_get(const struct mie_id_map *map, mie_id *id)
{
return get_id(&map->map_entries, id);
}

60
mie/include/mie/id.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef MIE_ID_H_
#define MIE_ID_H_
#include <blue/core/btree.h>
#include <blue/core/queue.h>
#include <blue/core/rope.h>
#include <mie/misc.h>
#include <stddef.h>
#include <stdint.h>
#define MIE_ID(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
{ \
.id_bytes = {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p}, \
}
#define MIE_ID_ZERO ((mie_id) {0})
#define MIE_ID_STRING_MAX 37
#define MIE_ID_NR_BYTES 16
#define MIE_ID_NR_WORDS (MIE_ID_NR_BYTES / sizeof(uintptr_t))
#define MIE_ID_NR_WORDS_16 (MIE_ID_NR_BYTES / sizeof(uint16_t))
#define MIE_ID_NR_WORDS_32 (MIE_ID_NR_BYTES / sizeof(uint32_t))
#define MIE_ID_NR_WORDS_64 (MIE_ID_NR_BYTES / sizeof(uint64_t))
typedef struct mie_id {
union {
uint8_t id_bytes[MIE_ID_NR_BYTES];
uintptr_t id_words[MIE_ID_NR_WORDS];
uint16_t id_words_16[MIE_ID_NR_WORDS_16];
uint32_t id_words_32[MIE_ID_NR_WORDS_32];
uint64_t id_words_64[MIE_ID_NR_WORDS_64];
};
b_btree_node e_node;
} mie_id;
struct mie_id_map {
mie_id map_ns_id;
b_btree map_entries;
};
MIE_API void mie_id_init(
mie_id *out, uint32_t a, uint16_t b, uint16_t c, uint16_t d, uint64_t e);
MIE_API void mie_id_init_zero(mie_id *out);
MIE_API void mie_id_init_random(mie_id *out);
MIE_API void mie_id_init_ns(mie_id *out, const mie_id *ns, const b_rope *name);
static inline int mie_id_compare(const mie_id *a, const mie_id *b)
{
return memcmp(a->id_bytes, b->id_bytes, sizeof a->id_bytes);
}
MIE_API void mie_id_to_string(const mie_id *id, char *out, size_t max);
MIE_API void mie_id_map_init(struct mie_id_map *map, const mie_id *ns);
MIE_API const mie_id *mie_id_map_get_ns(const struct mie_id_map *map);
MIE_API void mie_id_map_put(struct mie_id_map *map, mie_id *id, const b_rope *name);
MIE_API mie_id *mie_id_map_get(const struct mie_id_map *map, mie_id *id);
#endif