From 21bcbb7edcd369a81f56d571cd131f053d12a491 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 4 Jan 2026 14:03:12 +0000 Subject: [PATCH] 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. --- mie/id.c | 151 +++++++++++++++++++++++++++++++++++++++++++ mie/include/mie/id.h | 60 +++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 mie/id.c create mode 100644 mie/include/mie/id.h diff --git a/mie/id.c b/mie/id.c new file mode 100644 index 0000000..18e3e32 --- /dev/null +++ b/mie/id.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/mie/include/mie/id.h b/mie/include/mie/id.h new file mode 100644 index 0000000..68353d6 --- /dev/null +++ b/mie/include/mie/id.h @@ -0,0 +1,60 @@ +#ifndef MIE_ID_H_ +#define MIE_ID_H_ + +#include +#include +#include +#include +#include +#include + +#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