#include #include #include #include #include B_BTREE_DEFINE_SIMPLE_INSERT(struct mie_name_map_entry, e_node, e_hash, put_entry) 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_iterator it; b_queue_iterator_begin(&bucket->b_names, &it); while (b_queue_iterator_is_valid(&it)) { b_queue_iterator_erase(&it); } free(bucket); } struct mie_name_map *mie_name_map_create(void) { struct mie_name_map *out = malloc(sizeof *out); if (!out) { return NULL; } memset(out, 0x0, sizeof *out); return out; } void mie_name_map_destroy(struct mie_name_map *map) { b_btree_iterator it; b_btree_iterator_begin(&map->m_entries, &it); while (b_btree_iterator_is_valid(&it)) { struct mie_name_map_entry *entry = b_unbox(struct mie_name_map_entry, it.node, e_node); b_btree_iterator_erase(&it); if (entry->e_type == MIE_NAME_MAP_E_BUCKET) { struct mie_name_bucket *bucket = (struct mie_name_bucket *)entry; destroy_bucket(bucket); } } free(map); } static b_status put_name_in_bucket( struct mie_name_bucket *bucket, struct mie_name *name) { b_queue_iterator it; b_queue_foreach (&it, &bucket->b_names) { struct mie_name *cur = (struct mie_name *)b_unbox( struct mie_name_map_entry, it.entry, e_entry); if (!strcmp(cur->n_str, name->n_str)) { return B_ERR_NAME_EXISTS; } } b_queue_push_back(&bucket->b_names, &name->n_base.e_entry); 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); put_entry(&map->m_entries, &bucket->b_base); return B_SUCCESS; } 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_str = b_strdup(str); return entry; } } if (flags & MIE_NAME_MAP_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_string(str); b_status status = put_name(map, entry); if (B_OK(status)) { entry->n_str = b_strdup(str); b_rope_destroy(&unique_name); return entry; } } b_rope_destroy(&unique_name); return NULL; }