455 lines
12 KiB
C++
455 lines
12 KiB
C++
#include "core/defer.hpp"
|
|
#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"
|
|
#include "core/linked_list.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 <class T>
|
|
struct List_Node {
|
|
List_Node<T> *next;
|
|
List_Node<T> *prev;
|
|
int cap;
|
|
int len;
|
|
T data[];
|
|
};
|
|
|
|
template <class T>
|
|
struct List {
|
|
int block_size = 0;
|
|
int allocation_multiplier = 0;
|
|
List_Node<T> *first = 0;
|
|
List_Node<T> *last = 0;
|
|
List_Node<T> *first_free = 0;
|
|
|
|
struct Iter {
|
|
T *item;
|
|
List_Node<T> *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 <class T>
|
|
List_Node<T> *list_allocate_node(Allocator *arena, int size) {
|
|
auto node = (List_Node<T> *)allocate_size(arena, sizeof(List_Node<T>) + size * sizeof(T), false);
|
|
node->cap = size;
|
|
node->len = 0;
|
|
node->next = 0;
|
|
node->prev = 0;
|
|
return node;
|
|
}
|
|
|
|
template <class T>
|
|
void list_allocate_free_node(Allocator *arena, List<T> *list, int size) {
|
|
List_Node<T> *node = list_allocate_node<T>(arena, size);
|
|
DLL_STACK_ADD(list->first_free, node);
|
|
}
|
|
|
|
template <class T>
|
|
void list_make_sure_there_is_room_for_item_count(Allocator *arena, List<T> *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<T> *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<T>(arena, list->block_size);
|
|
list->block_size *= list->allocation_multiplier;
|
|
}
|
|
|
|
assert(node);
|
|
DLL_QUEUE_ADD(list->first, list->last, node);
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
T *list_get(List<T> *list, int index, List_Node<T> **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 <class T>
|
|
T *getp(List<T> *list, int index) {
|
|
return list_get(list, index);
|
|
}
|
|
|
|
template <class T>
|
|
T get(List<T> *list, int index) {
|
|
return *list_get(list, index);
|
|
}
|
|
|
|
template <class T>
|
|
void add(Allocator *arena, List<T> *list, T item) {
|
|
list_make_sure_there_is_room_for_item_count(arena, list, 1);
|
|
list->last->data[list->last->len++] = item;
|
|
}
|
|
|
|
template <class T>
|
|
T *add_size(Allocator *arena, List<T> *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 <class T>
|
|
void list_free_node(List<T> *list, List_Node<T> *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 <class T>
|
|
int length(List<T> *list) {
|
|
int result = 0;
|
|
For_Linked_List(list->first) {
|
|
result += it->len;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
void free_all_nodes(List<T> *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 <class T>
|
|
T ordered_remove(List<T> *list, int index) {
|
|
List_Node<T> *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 <class T>
|
|
T unordered_remove(List<T> *list, int index) {
|
|
List_Node<T> *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 <class T>
|
|
T pop(List<T> *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 <class T>
|
|
T *merge(Allocator *arena, List<T> *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;
|
|
}
|