Files
mie/mie/name.c
Max Wash 55161cd6c8 mie: re-implement vectors of blocks and registers using vector move callbacks
vector move callbacks allow mie_name instances to be stored in movable memory,
as the internal bst pointers can now be fixed during vector resize operations.
2026-01-18 22:58:17 +00:00

335 lines
7.3 KiB
C

#include <blue/core/rope.h>
#include <blue/ds/string.h>
#include <mie/name.h>
#include <stdlib.h>
#include <string.h>
static B_BTREE_DEFINE_SIMPLE_INSERT(
struct mie_name_map_entry, e_node, e_hash, put_entry);
static B_BTREE_DEFINE_SIMPLE_GET(
struct mie_name_map_entry, uint64_t, e_node, e_hash, get_entry);
static struct mie_name_bucket *create_bucket(void)
{
struct mie_name_bucket *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->b_base.e_type = MIE_NAME_MAP_E_BUCKET;
return out;
}
static void destroy_bucket(struct mie_name_bucket *bucket)
{
b_queue_entry *entry = b_queue_first(&bucket->b_names);
while (entry) {
b_queue_entry *next = b_queue_next(entry);
b_queue_delete(&bucket->b_names, entry);
entry = next;
}
free(bucket);
}
struct mie_name_map *mie_name_map_create(const struct mie_name_map *parent)
{
struct mie_name_map *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->m_parent = parent;
return out;
}
void mie_name_map_destroy(struct mie_name_map *map)
{
b_btree_node *node = b_btree_first(&map->m_entries);
while (node) {
struct mie_name_map_entry *entry
= b_unbox(struct mie_name_map_entry, node, e_node);
b_btree_node *next = b_btree_next(node);
b_btree_delete(&map->m_entries, node);
if (entry->e_type == MIE_NAME_MAP_E_BUCKET) {
struct mie_name_bucket *bucket
= (struct mie_name_bucket *)entry;
destroy_bucket(bucket);
}
node = next;
}
free(map);
}
static b_status put_name_in_bucket(
struct mie_name_bucket *bucket, struct mie_name *name)
{
b_queue_entry *entry = b_queue_first(&bucket->b_names);
while (entry) {
struct mie_name *cur = (struct mie_name *)b_unbox(
struct mie_name_map_entry, entry, e_entry);
if (!strcmp(cur->n_str, name->n_str)) {
return B_ERR_NAME_EXISTS;
}
entry = b_queue_next(entry);
}
b_queue_push_back(&bucket->b_names, &name->n_base.e_entry);
name->n_bucket = bucket;
return B_SUCCESS;
}
static b_status put_name(struct mie_name_map *map, struct mie_name *name)
{
struct mie_name_map_entry *entry
= get_entry(&map->m_entries, name->n_base.e_hash);
if (!entry) {
put_entry(&map->m_entries, &name->n_base);
return B_SUCCESS;
}
if (entry->e_type == MIE_NAME_MAP_E_BUCKET) {
struct mie_name_bucket *bucket = (struct mie_name_bucket *)entry;
return put_name_in_bucket(bucket, name);
}
struct mie_name *existing_name = (struct mie_name *)entry;
if (!strcmp(existing_name->n_str, name->n_str)) {
return B_ERR_NAME_EXISTS;
}
struct mie_name_bucket *bucket = create_bucket();
if (!bucket) {
return B_ERR_NO_MEMORY;
}
b_btree_delete(&map->m_entries, &entry->e_node);
entry->e_entry = B_QUEUE_ENTRY_INIT;
bucket->b_base.e_hash = name->n_base.e_hash;
b_queue_push_back(&bucket->b_names, &existing_name->n_base.e_entry);
b_queue_push_back(&bucket->b_names, &name->n_base.e_entry);
existing_name->n_bucket = name->n_bucket = bucket;
put_entry(&map->m_entries, &bucket->b_base);
return B_SUCCESS;
}
static bool check_name(const struct mie_name *name, const char *s, uint64_t hash)
{
return name->n_base.e_hash == hash && !strcmp(name->n_str, s);
}
static const struct mie_name *find_name_in_bucket(
const struct mie_name_bucket *bucket, const char *s, uint64_t hash)
{
b_queue_entry *entry = b_queue_first(&bucket->b_names);
while (entry) {
struct mie_name_map_entry *map_entry
= b_unbox(struct mie_name_map_entry, entry, e_entry);
struct mie_name *name = (struct mie_name *)map_entry;
if (check_name(name, s, hash)) {
return name;
}
entry = b_queue_next(entry);
}
return NULL;
}
static const struct mie_name *name_map_get(
const struct mie_name_map *map, const char *name, uint64_t name_hash)
{
const struct mie_name_map_entry *entry
= get_entry(&map->m_entries, name_hash);
if (!entry) {
return NULL;
}
const struct mie_name *name_entry = NULL;
struct mie_name_bucket *bucket = NULL;
switch (entry->e_type) {
case MIE_NAME_MAP_E_NAME:
name_entry = (struct mie_name *)entry;
if (check_name(name_entry, name, name_hash)) {
return name_entry;
}
break;
case MIE_NAME_MAP_E_BUCKET:
bucket = (struct mie_name_bucket *)entry;
name_entry = find_name_in_bucket(bucket, name, name_hash);
if (name_entry) {
return name_entry;
}
break;
default:
break;
}
return NULL;
}
const struct mie_name *mie_name_map_get(
const struct mie_name_map *map, const char *name,
enum mie_name_map_flags flags)
{
uint64_t name_hash = b_hash_cstr(name);
while (map) {
const struct mie_name *result = name_map_get(map, name, name_hash);
if (result) {
return result;
}
map = (flags & MIE_NAME_MAP_F_RECURSIVE) ? map->m_parent : NULL;
}
return NULL;
}
struct mie_name *mie_name_map_put(
struct mie_name_map *map, struct mie_name *entry, const char *hint,
enum mie_name_map_flags flags)
{
memset(entry, 0x0, sizeof *entry);
entry->n_base.e_type = MIE_NAME_MAP_E_NAME;
b_rope base = {};
if (hint) {
b_rope_init_cstr_borrowed(&base, hint);
}
char str[256];
if (hint) {
/* first try just the hint on its own */
b_rope_to_cstr(&base, str, sizeof str);
entry->n_str = str;
entry->n_base.e_hash = base.r_v.v_cstr.hash;
b_status status = put_name(map, entry);
if (B_OK(status)) {
entry->n_parent = map;
entry->n_str = b_strdup(str);
return entry;
}
}
if (flags & MIE_NAME_MAP_F_STRICT) {
/* the caller insists that `hint` be used as the name.
* such a name already exists, so we can't help them */
return NULL;
}
/* that name already exists, use a suffix to make the name unique.
* alternately, no hint was specified, so it's up to us to generate the name */
b_rope dot = B_ROPE_CHAR('.');
b_rope suffix = B_ROPE_UINT(0);
b_rope unique_name;
if (hint) {
const b_rope *parts[] = {&base, &dot, &suffix};
b_rope_join(&unique_name, parts, sizeof parts / sizeof parts[0]);
} else {
b_rope_concat(&unique_name, &base, &suffix);
}
size_t i = 0;
if (!hint || hint[0] == '\0') {
i = map->m_next_id++;
}
for (;; i++) {
suffix.r_v.v_uint = i;
b_rope_to_cstr(&unique_name, str, sizeof str);
entry->n_str = str;
entry->n_base.e_hash = b_hash_cstr(str);
b_status status = put_name(map, entry);
if (B_OK(status)) {
entry->n_parent = map;
entry->n_str = b_strdup(str);
b_rope_destroy(&unique_name);
return entry;
}
}
b_rope_destroy(&unique_name);
return NULL;
}
void mie_name_move(struct mie_name *dst, struct mie_name *src)
{
if (!src->n_parent) {
memmove(dst, src, sizeof *src);
return;
}
memmove(dst, src, sizeof *src);
if (src->n_bucket) {
b_queue_move(
&src->n_bucket->b_names, &dst->n_base.e_entry,
&src->n_base.e_entry);
} else {
b_btree_move(
&src->n_parent->m_entries, &dst->n_base.e_node,
&src->n_base.e_node);
}
}
void mie_name_destroy(struct mie_name *name)
{
struct mie_name_map *parent = name->n_parent;
struct mie_name_map_entry *entry
= get_entry(&parent->m_entries, name->n_base.e_hash);
struct mie_name_bucket *bucket = NULL;
if (!entry) {
return;
}
switch (entry->e_type) {
case MIE_NAME_MAP_E_NAME:
b_btree_delete(&parent->m_entries, &entry->e_node);
break;
case MIE_NAME_MAP_E_BUCKET:
bucket = b_unbox(struct mie_name_bucket, entry, b_base);
b_queue_delete(&bucket->b_names, &name->n_base.e_entry);
break;
default:
abort();
return;
}
name->n_parent = NULL;
if (name->n_str) {
free(name->n_str);
name->n_str = NULL;
}
}