mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-09 06:23:14 +00:00
302 lines
7.7 KiB
C
302 lines
7.7 KiB
C
/*
|
|
Example of usage:
|
|
|
|
#define MAP_TYPE String
|
|
#define MAP_FUNC map_string_
|
|
#define MAP_NAME MapString
|
|
#include "map.c"
|
|
*/
|
|
|
|
#ifndef MAP_FIND_RESULT
|
|
#define MAP_FIND_RESULT
|
|
// NOTE(bill): This is the same for every `Map`
|
|
typedef struct MapFindResult {
|
|
isize hash_index;
|
|
isize entry_prev;
|
|
isize entry_index;
|
|
} MapFindResult;
|
|
#endif
|
|
|
|
#define _J2(a,b) GB_JOIN2(a,b)
|
|
|
|
/*
|
|
MAP_TYPE - Entry type
|
|
MAP_FUNC - Function prefix (e.g. entity_map_)
|
|
MAP_NAME - Name of Map (e.g. EntityMap)
|
|
*/
|
|
#define MAP_ENTRY _J2(MAP_NAME,Entry)
|
|
|
|
typedef struct MAP_ENTRY {
|
|
HashKey key;
|
|
isize next;
|
|
MAP_TYPE value;
|
|
} MAP_ENTRY;
|
|
|
|
typedef struct MAP_NAME {
|
|
Array(isize) hashes;
|
|
Array(MAP_ENTRY) entries;
|
|
} MAP_NAME;
|
|
|
|
void _J2(MAP_FUNC,init) (MAP_NAME *h, gbAllocator a);
|
|
void _J2(MAP_FUNC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity);
|
|
void _J2(MAP_FUNC,destroy) (MAP_NAME *h);
|
|
MAP_TYPE *_J2(MAP_FUNC,get) (MAP_NAME *h, HashKey key);
|
|
void _J2(MAP_FUNC,set) (MAP_NAME *h, HashKey key, MAP_TYPE value);
|
|
void _J2(MAP_FUNC,remove) (MAP_NAME *h, HashKey key);
|
|
void _J2(MAP_FUNC,clear) (MAP_NAME *h);
|
|
void _J2(MAP_FUNC,grow) (MAP_NAME *h);
|
|
void _J2(MAP_FUNC,rehash) (MAP_NAME *h, isize new_count);
|
|
|
|
#if 1
|
|
MAP_ENTRY *_J2(MAP_FUNC,multi_find_first)(MAP_NAME *h, HashKey key);
|
|
MAP_ENTRY *_J2(MAP_FUNC,multi_find_next) (MAP_NAME *h, MAP_ENTRY *e);
|
|
|
|
isize _J2(MAP_FUNC,multi_count) (MAP_NAME *h, HashKey key);
|
|
void _J2(MAP_FUNC,multi_get_all) (MAP_NAME *h, HashKey key, MAP_TYPE *items);
|
|
void _J2(MAP_FUNC,multi_insert) (MAP_NAME *h, HashKey key, MAP_TYPE value);
|
|
void _J2(MAP_FUNC,multi_remove) (MAP_NAME *h, HashKey key, MAP_ENTRY *e);
|
|
void _J2(MAP_FUNC,multi_remove_all)(MAP_NAME *h, HashKey key);
|
|
#endif
|
|
|
|
|
|
|
|
gb_inline void _J2(MAP_FUNC,init)(MAP_NAME *h, gbAllocator a) {
|
|
array_init(&h->hashes, a);
|
|
array_init(&h->entries, a);
|
|
}
|
|
|
|
gb_inline void _J2(MAP_FUNC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity) {
|
|
array_init_reserve(&h->hashes, a, capacity);
|
|
array_init_reserve(&h->entries, a, capacity);
|
|
}
|
|
|
|
gb_inline void _J2(MAP_FUNC,destroy)(MAP_NAME *h) {
|
|
array_free(&h->entries);
|
|
array_free(&h->hashes);
|
|
}
|
|
|
|
gb_internal isize _J2(MAP_FUNC,_add_entry)(MAP_NAME *h, HashKey key) {
|
|
MAP_ENTRY e = {0};
|
|
e.key = key;
|
|
e.next = -1;
|
|
array_add(&h->entries, e);
|
|
return h->entries.count-1;
|
|
}
|
|
|
|
gb_internal MapFindResult _J2(MAP_FUNC,_find)(MAP_NAME *h, HashKey key) {
|
|
MapFindResult fr = {-1, -1, -1};
|
|
if (h->hashes.count > 0) {
|
|
fr.hash_index = key.key % h->hashes.count;
|
|
fr.entry_index = h->hashes.e[fr.hash_index];
|
|
while (fr.entry_index >= 0) {
|
|
if (hash_key_equal(h->entries.e[fr.entry_index].key, key)) {
|
|
return fr;
|
|
}
|
|
fr.entry_prev = fr.entry_index;
|
|
fr.entry_index = h->entries.e[fr.entry_index].next;
|
|
}
|
|
}
|
|
return fr;
|
|
}
|
|
|
|
gb_internal MapFindResult _J2(MAP_FUNC,_find_from_entry)(MAP_NAME *h, MAP_ENTRY *e) {
|
|
MapFindResult fr = {-1, -1, -1};
|
|
if (h->hashes.count > 0) {
|
|
fr.hash_index = e->key.key % h->hashes.count;
|
|
fr.entry_index = h->hashes.e[fr.hash_index];
|
|
while (fr.entry_index >= 0) {
|
|
if (&h->entries.e[fr.entry_index] == e) {
|
|
return fr;
|
|
}
|
|
fr.entry_prev = fr.entry_index;
|
|
fr.entry_index = h->entries.e[fr.entry_index].next;
|
|
}
|
|
}
|
|
return fr;
|
|
}
|
|
|
|
|
|
gb_internal b32 _J2(MAP_FUNC,_full)(MAP_NAME *h) {
|
|
return 0.75f * h->hashes.count <= h->entries.count;
|
|
}
|
|
|
|
gb_inline void _J2(MAP_FUNC,grow)(MAP_NAME *h) {
|
|
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
|
|
_J2(MAP_FUNC,rehash)(h, new_count);
|
|
}
|
|
|
|
void _J2(MAP_FUNC,rehash)(MAP_NAME *h, isize new_count) {
|
|
isize i, j;
|
|
MAP_NAME nh = {0};
|
|
_J2(MAP_FUNC,init)(&nh, h->hashes.allocator);
|
|
array_resize(&nh.hashes, new_count);
|
|
array_reserve(&nh.entries, h->entries.count);
|
|
for (i = 0; i < new_count; i++) {
|
|
nh.hashes.e[i] = -1;
|
|
}
|
|
for (i = 0; i < h->entries.count; i++) {
|
|
MAP_ENTRY *e = &h->entries.e[i];
|
|
MapFindResult fr;
|
|
if (nh.hashes.count == 0) {
|
|
_J2(MAP_FUNC,grow)(&nh);
|
|
}
|
|
fr = _J2(MAP_FUNC,_find)(&nh, e->key);
|
|
j = _J2(MAP_FUNC,_add_entry)(&nh, e->key);
|
|
if (fr.entry_prev < 0) {
|
|
nh.hashes.e[fr.hash_index] = j;
|
|
} else {
|
|
nh.entries.e[fr.entry_prev].next = j;
|
|
}
|
|
nh.entries.e[j].next = fr.entry_index;
|
|
nh.entries.e[j].value = e->value;
|
|
if (_J2(MAP_FUNC,_full)(&nh)) {
|
|
_J2(MAP_FUNC,grow)(&nh);
|
|
}
|
|
}
|
|
_J2(MAP_FUNC,destroy)(h);
|
|
*h = nh;
|
|
}
|
|
|
|
gb_inline MAP_TYPE *_J2(MAP_FUNC,get)(MAP_NAME *h, HashKey key) {
|
|
isize index = _J2(MAP_FUNC,_find)(h, key).entry_index;
|
|
if (index >= 0) {
|
|
return &h->entries.e[index].value;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void _J2(MAP_FUNC,set)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
|
isize index;
|
|
MapFindResult fr;
|
|
if (h->hashes.count == 0)
|
|
_J2(MAP_FUNC,grow)(h);
|
|
fr = _J2(MAP_FUNC,_find)(h, key);
|
|
if (fr.entry_index >= 0) {
|
|
index = fr.entry_index;
|
|
} else {
|
|
index = _J2(MAP_FUNC,_add_entry)(h, key);
|
|
if (fr.entry_prev >= 0) {
|
|
h->entries.e[fr.entry_prev].next = index;
|
|
} else {
|
|
h->hashes.e[fr.hash_index] = index;
|
|
}
|
|
}
|
|
h->entries.e[index].value = value;
|
|
|
|
if (_J2(MAP_FUNC,_full)(h)) {
|
|
_J2(MAP_FUNC,grow)(h);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void _J2(MAP_FUNC,_erase)(MAP_NAME *h, MapFindResult fr) {
|
|
if (fr.entry_prev < 0) {
|
|
h->hashes.e[fr.hash_index] = h->entries.e[fr.entry_index].next;
|
|
} else {
|
|
h->entries.e[fr.entry_prev].next = h->entries.e[fr.entry_index].next;
|
|
}
|
|
if (fr.entry_index == h->entries.count-1) {
|
|
array_pop(&h->entries);
|
|
return;
|
|
}
|
|
h->entries.e[fr.entry_index] = h->entries.e[h->entries.count-1];
|
|
MapFindResult last = _J2(MAP_FUNC,_find)(h, h->entries.e[fr.entry_index].key);
|
|
if (last.entry_prev >= 0) {
|
|
h->entries.e[last.entry_prev].next = fr.entry_index;
|
|
} else {
|
|
h->hashes.e[last.hash_index] = fr.entry_index;
|
|
}
|
|
}
|
|
|
|
void _J2(MAP_FUNC,remove)(MAP_NAME *h, HashKey key) {
|
|
MapFindResult fr = _J2(MAP_FUNC,_find)(h, key);
|
|
if (fr.entry_index >= 0) {
|
|
_J2(MAP_FUNC,_erase)(h, fr);
|
|
}
|
|
}
|
|
|
|
gb_inline void _J2(MAP_FUNC,clear)(MAP_NAME *h) {
|
|
array_clear(&h->hashes);
|
|
array_clear(&h->entries);
|
|
}
|
|
|
|
|
|
#if 1
|
|
MAP_ENTRY *_J2(MAP_FUNC,multi_find_first)(MAP_NAME *h, HashKey key) {
|
|
isize i = _J2(MAP_FUNC,_find)(h, key).entry_index;
|
|
if (i < 0) {
|
|
return NULL;
|
|
}
|
|
return &h->entries.e[i];
|
|
}
|
|
|
|
MAP_ENTRY *_J2(MAP_FUNC,multi_find_next)(MAP_NAME *h, MAP_ENTRY *e) {
|
|
isize i = e->next;
|
|
while (i >= 0) {
|
|
if (hash_key_equal(h->entries.e[i].key, e->key)) {
|
|
return &h->entries.e[i];
|
|
}
|
|
i = h->entries.e[i].next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
isize _J2(MAP_FUNC,multi_count)(MAP_NAME *h, HashKey key) {
|
|
isize count = 0;
|
|
MAP_ENTRY *e = _J2(MAP_FUNC,multi_find_first)(h, key);
|
|
while (e != NULL) {
|
|
count++;
|
|
e = _J2(MAP_FUNC,multi_find_next)(h, e);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void _J2(MAP_FUNC,multi_get_all)(MAP_NAME *h, HashKey key, MAP_TYPE *items) {
|
|
isize i = 0;
|
|
MAP_ENTRY *e = _J2(MAP_FUNC,multi_find_first)(h, key);
|
|
while (e != NULL) {
|
|
items[i++] = e->value;
|
|
e = _J2(MAP_FUNC,multi_find_next)(h, e);
|
|
}
|
|
}
|
|
|
|
void _J2(MAP_FUNC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
|
if (h->hashes.count == 0) {
|
|
_J2(MAP_FUNC,grow)(h);
|
|
}
|
|
MapFindResult fr = _J2(MAP_FUNC,_find)(h, key);
|
|
isize i = _J2(MAP_FUNC,_add_entry)(h, key);
|
|
if (fr.entry_prev < 0) {
|
|
h->hashes.e[fr.hash_index] = i;
|
|
} else {
|
|
h->entries.e[fr.entry_prev].next = i;
|
|
}
|
|
h->entries.e[i].next = fr.entry_index;
|
|
h->entries.e[i].value = value;
|
|
if (_J2(MAP_FUNC,_full)(h)) {
|
|
_J2(MAP_FUNC,grow)(h);
|
|
}
|
|
}
|
|
|
|
void _J2(MAP_FUNC,multi_remove)(MAP_NAME *h, HashKey key, MAP_ENTRY *e) {
|
|
MapFindResult fr = _J2(MAP_FUNC,_find_from_entry)(h, e);
|
|
if (fr.entry_index >= 0) {
|
|
_J2(MAP_FUNC,_erase)(h, fr);
|
|
}
|
|
}
|
|
|
|
void _J2(MAP_FUNC,multi_remove_all)(MAP_NAME *h, HashKey key) {
|
|
while (_J2(MAP_FUNC,get)(h, key) != NULL) {
|
|
_J2(MAP_FUNC,remove)(h, key);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#undef _J2
|
|
#undef MAP_TYPE
|
|
#undef MAP_FUNC
|
|
#undef MAP_NAME
|
|
#undef MAP_ENTRY
|