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:
151
mie/id.c
Normal file
151
mie/id.c
Normal 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
60
mie/include/mie/id.h
Normal 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
|
||||||
Reference in New Issue
Block a user