#define ARRAY_ALLOCATOR_TYPE Allocator #define ARRAY_ASSERT assert #define ARRAY_ALLOCATE(allocator, size) allocate_size(allocator, size) #define ARRAY_DEALLOCATE(allocator, p) deallocate(allocator, p) #include "core/array.hpp" //----------------------------------------------------------------------------- // Map //----------------------------------------------------------------------------- struct Map_Key_Value { int occupied; U64 key; void *value; }; struct Map { Allocator *allocator; Map_Key_Value *data; S64 len; S64 cap; }; CORE_Static void map_insert(Map *map, U64 key, void *val); CORE_Static void map_grow(Map *map, S64 new_size) { new_size = max((S64)16, new_size); assert(new_size > map->cap); assert(is_pow2(new_size)); assert(map->allocator); Map new_map = {}; new_map.data = allocate_array(map->allocator, Map_Key_Value, new_size); new_map.cap = new_size; new_map.allocator = map->allocator; for (S64 i = 0; i < map->cap; i++) { if (map->data[i].occupied) { map_insert(&new_map, map->data[i].key, map->data[i].value); } } if (map->data) deallocate(map->allocator, map->data); *map = new_map; } CORE_Static Map map_make(Allocator *a, S64 size) { Map result = {a}; map_grow(&result, size); return result; } CORE_Static void map_insert(Map *map, U64 key, void *val) { assert(val); assert(key); // if(key == 0) key+=1; if ((2 * map->len) + 1 > map->cap) { map_grow(map, 2 * map->cap); } U64 hash = hash_u64(key); U64 index = wrap_around_pow2(hash, map->cap); U64 i = index; for (;;) { if (map->data[i].occupied == false) { map->len++; map->data[i].occupied = true; map->data[i].key = key; map->data[i].value = val; return; } else if (map->data[i].key == key) { map->data[i].value = val; return; } i = wrap_around_pow2(i + 1, map->cap); if (i == map->cap) { return; } } } CORE_Static Map_Key_Value * map_base_get(Map *map, U64 key) { if (map->len == 0) return 0; assert(key); U64 hash = hash_u64(key); U64 index = wrap_around_pow2(hash, map->cap); U64 i = index; for (;;) { if (map->data[i].key == key) { return map->data + i; } else if (map->data[i].key == 0) { return 0; } i = wrap_around_pow2(i + 1, map->cap); if (i == map->cap) { return 0; } } } CORE_Static void * map_get(Map *map, U64 key) { Map_Key_Value *result = map_base_get(map, key); if (result && result->occupied) return result->value; return 0; } CORE_Static void * map_remove(Map *map, U64 key) { Map_Key_Value *kv = map_base_get(map, key); if (kv) { kv->occupied = false; return kv->value; } return 0; } CORE_Static void * map_get(Map *map, void *pointer) { return map_get(map, (U64)pointer); } CORE_Static void * map_get(Map *map, Intern_String string) { return map_get(map, hash_string(string.s)); } CORE_Static void map_insert(Map *map, void *key, void *value) { map_insert(map, (U64)key, value); } CORE_Static void map_insert(Map *map, Intern_String key, void *value) { map_insert(map, hash_string(key.s), value); } //----------------------------------------------------------------------------- // String intern //----------------------------------------------------------------------------- struct Intern_Table { Allocator *string_allocator; Map map; U8 *first_keyword; U8 *last_keyword; }; CORE_Static Intern_Table intern_table_make(Allocator *string_allocator, Allocator *map_allocator, S64 initial_size = 32) { Intern_Table result = {}; result.map = map_make(map_allocator, initial_size); result.string_allocator = string_allocator; return result; } CORE_Static Intern_String intern_string(Intern_Table *t, String string) { assert(t->string_allocator); U64 hash = hash_string(string); U8 *slot = (U8 *)map_get(&t->map, hash); if (slot) { // @todo: Is this a cast bug: *(slot-sizeof(S64))? slot is u8 so truncates? Intern_String result = { {slot, *(slot - sizeof(S64))} }; return result; } S64 *len_address = (S64 *)allocate_size(t->string_allocator, string.len + 1 + sizeof(S64), false); *len_address = string.len; U8 *string_address = (U8 *)(len_address + 1); memory_copy(string_address, string.str, string.len); string_address[string.len] = 0; map_insert(&t->map, hash, string_address); Intern_String result = { {string_address, *len_address} }; return result; } //----------------------------------------------------------------------------- // Array List //----------------------------------------------------------------------------- const int LIST_DEFAULT_BLOCK_SIZE = 16; const int LIST_DEFAULT_ALLOCATION_MUL = 2; template struct List_Node { List_Node *next; List_Node *prev; int cap; int len; T data[]; }; template struct List { int block_size = 0; int allocation_multiplier = 0; List_Node *first = 0; List_Node *last = 0; List_Node *first_free = 0; struct Iter { T *item; List_Node *node; int node_index; T &operator*() { return *item; } Iter &operator++() { if (node) { if (node_index + 1 >= node->len) { node = node->next; node_index = -1; item = 0; } if (node) { node_index += 1; item = node->data + node_index; } } return *this; } }; Iter begin() { Iter result = {}; result.node = first; result.node_index = -1; return ++result; } Iter end() { return {}; } friend bool operator!=(Iter &a, Iter &b) { return a.item != b.item; } }; template List_Node *list_allocate_node(Allocator *arena, int size) { auto node = (List_Node *)allocate_size(arena, sizeof(List_Node) + size * sizeof(T), false); node->cap = size; node->len = 0; node->next = 0; node->prev = 0; return node; } template void list_allocate_free_node(Allocator *arena, List *list, int size) { List_Node *node = list_allocate_node(arena, size); DLL_STACK_ADD(list->first_free, node); } template void list_make_sure_there_is_room_for_item_count(Allocator *arena, List *list, int item_count) { if (list->last == 0 || list->last->len + item_count > list->last->cap) { // Not enough space we need to get a new block List_Node *node = 0; // Iterate the free list to check if we have a block of required size there For_Linked_List(list->first_free) { if (it->cap >= item_count) { DLL_STACK_REMOVE(list->first_free, it); node = it; node->len = 0; break; } } // We don't have a block on the free list need to allocate if (!node) { // Set default values if not initialized if (!list->allocation_multiplier) list->allocation_multiplier = LIST_DEFAULT_ALLOCATION_MUL; if (!list->block_size) list->block_size = LIST_DEFAULT_BLOCK_SIZE; if (item_count > list->block_size) list->block_size = item_count * 2; node = list_allocate_node(arena, list->block_size); list->block_size *= list->allocation_multiplier; } assert(node); DLL_QUEUE_ADD_LAST(list->first, list->last, node); } } template T *list_get(List *list, int index, List_Node **node = 0, int *in_block_index = 0) { if (list) { int i = 0; For_Linked_List(list->first) { int lookup_i = index - i; if (lookup_i < it->len) { if (node) *node = it; if (in_block_index) *in_block_index = lookup_i; return it->data + lookup_i; } i += it->len; } } return 0; } template T *getp(List *list, int index) { return list_get(list, index); } template T get(List *list, int index) { return *list_get(list, index); } template void add(Allocator *arena, List *list, T item) { list_make_sure_there_is_room_for_item_count(arena, list, 1); list->last->data[list->last->len++] = item; } template T *add_size(Allocator *arena, List *list, int count = 1, int zero_memory = 0) { list_make_sure_there_is_room_for_item_count(arena, list, count); T *result = list->last->data + list->last->len; list->last->len += count; if (zero_memory) { memory_zero(result, sizeof(T) * count); } return result; } template void list_free_node(List *list, List_Node *node) { #if 1 // Make sure it's actually in list list bool found = false; For_Linked_List(list->first) { if (it == node) { found = true; break; } } assert(found); #endif DLL_QUEUE_REMOVE(list->first, list->last, node); DLL_STACK_ADD(list->first_free, node); } template int length(List *list) { int result = 0; For_Linked_List(list->first) { result += it->len; } return result; } template void free_all_nodes(List *list) { if (list->first == 0) return; assert(!list->last->next); assert(!list->first->prev); list->last->next = list->first_free; if (list->first_free) list->first_free->prev = list->last; list->first_free = list->first; list->last = list->first = 0; } template T ordered_remove(List *list, int index) { List_Node *node; int in_block_index; T *data = list_get(list, index, &node, &in_block_index); assert_message(data, "Trying to unordered_remove element that's outside of the List"); if (!data) return {}; T result = *data; // Check if we need to deallocate the block if (node->len == 1) { list_free_node(list, node); return result; } // We need to move part of the block to fill the new empty spot int right_count = (--node->len) - in_block_index; memory_copy(data, data + 1, sizeof(T) * right_count); return result; } template T unordered_remove(List *list, int index) { List_Node *node; T *data = list_get(list, index, &node); assert_message(data, "Trying to unordered_remove element that's outside of the List"); if (!data) return {}; assert(node->len); assert(node->cap); // Swap T result = *data; *data = node->data[node->len - 1]; node->len -= 1; if (node->len == 0) { list_free_node(list, node); } return result; } template T pop(List *list) { assert(list->last != 0); assert(list->last->len > 0); T result = list->last->data[--list->last->len]; if (list->last->len == 0) { list_free_node(list, list->last); } return result; } template T *merge(Allocator *arena, List *list) { int len = length(list); T *result = allocate_size(arena, T, len, false); int i = 0; For_Linked_List(list->first) { memory_copy(result + i, it->data, it->len * sizeof(T)); i += it->len; } return result; }