function S64 clamp_top_s64(S64 val, S64 max){ if(val>max)return max; return val; } function SizeU max_sizeu(SizeU a, SizeU b){ if(a>b) return a; return b; } function U64 hash_fnv(String string) { U64 hash = (U64)14695981039346656037ULL; for (U64 i = 0; i < string.len; i++) { hash = hash ^ (U64)(string.str[i]); hash = hash * (U64)1099511628211ULL; } return hash; } function U64 hash_u64(U64 x) { x *= 0xff51afd7ed558ccd; x ^= x >> 32; return x; } function U64 hash_ptr(const void *ptr) { return hash_u64((uintptr_t)ptr); } function U64 hash_mix(U64 x, U64 y) { x ^= y; x *= 0xff51afd7ed558ccd; x ^= x >> 32; return x; } function U64 is_pow2(U64 x) { assert(x != 0); B32 result = (x & (x - 1llu)) == 0; return result; } function U64 wrap_around_pow2(U64 x, U64 power_of_2) { assert(is_pow2(power_of_2)); U64 r = (((x)&((power_of_2)-1llu))); return r; } //----------------------------------------------------------------------------- // Strings //----------------------------------------------------------------------------- global String string_empty; function B32 string_compare(String a, String b){ if(a.len != b.len) return false; for(S64 i = 0; i < a.len; i++){ if(a.str[i] != b.str[i]) return false; } return true; } function U8 char_to_lower(U8 c){ if(c >= 'A' && c <= 'Z') c += 32; return c; } function U8 char_to_upper(U8 c){ if(c >= 'a' && c <= 'z') c -= 32; return c; } function B32 string_is_empty(String a){ B32 result = a.len == 0; return result; } function String string_to_lower(Arena *arena, String string){ String result = arena_push_string_copy(arena, string); for(S64 i = 0; i < string.len; i++){ result.str[i] = char_to_lower(result.str[i]); } return result; } //----------------------------------------------------------------------------- // Per Vognsen's like Map to pointers //----------------------------------------------------------------------------- typedef struct Map_Key_Val{ U64 key; void *value; }Map_Key_Val; typedef struct Map{ Map_Key_Val *data; int len; int cap; }Map; function void map_insert_u64(Map *map, U64 key, void *val); function int max_int(int a, int b); #include function void map_grow(Map *map, int new_size){ new_size = max_int(16, new_size); assert(new_size > map->cap); assert(is_pow2(new_size)); Map new_map = { .data = calloc(new_size, sizeof(Map_Key_Val)), .cap = new_size, }; for(int i = 0; i < map->cap; i++){ if(map->data[i].key){ map_insert_u64(&new_map, map->data[i].key, map->data[i].value); } } if(map->data) free(map->data); *map = new_map; } function void map_insert_u64(Map *map, U64 key, void *val){ assert(val); if(key == 0) key++; 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].key == 0){ map->len++; 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; } } } function void * map_get_u64(Map *map, U64 key){ if(map->len == 0) return 0; if(key == 0) 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].value; } else if(map->data[i].key == 0){ return 0; } i = wrap_around_pow2(i+1, map->cap); if(i == map->cap){ return 0; } } } function void * map_get(Map *map, void *pointer){ return map_get_u64(map, (U64)pointer); } function void map_insert(Map *map, void *key, void *value){ map_insert_u64(map, (U64)key, value); } function void map_test(){ Map map = {0}; const SizeU size = 1025; for(SizeU i = 1; i < size; i++){ map_insert_u64(&map, i, (void *)i); } for(SizeU i = 1; i < size; i++){ SizeU val = (SizeU)map_get_u64(&map, i); assert(val == i); } } //----------------------------------------------------------------------------- // Array //----------------------------------------------------------------------------- typedef struct Array_Head{ int cap, len; }Array_Head; #define array_get_head(x) (((Array_Head *)(x)) - 1) #define array_cap(x) array_get_head(x)->cap #define array_len(x) array_get_head(x)->len #define array_push(arr,i) (array_grow((void **)&arr, sizeof(arr[0])), (arr)[array_len(arr)++] = (i)) #define array_init(arr,cap) array__init((void **)&arr,sizeof(arr[0]), cap) function void array__init(void **array, SizeU sizeof_item, int cap){ Array_Head *head = malloc(sizeof_item*cap + sizeof(Array_Head)); head->cap = cap; head->len = 0; *array = head + 1; } function void array_grow(void **array, SizeU sizeof_item){ if(*array == 0){ array__init(array, sizeof_item, 16); } else if(array_len(*array) + 1 > array_cap(*array)){ Array_Head *head = array_get_head(*array); SizeU len = head->len; SizeU cap = head->cap * 2; head = realloc(head, sizeof_item*cap + sizeof(Array_Head)); head->cap = cap; head->len = len; *array = head + 1; } } function void array_test(){ int *array = 0; for(int i = 0; i < 100; i++){ array_push(array, i); } for(int i = 0; i < 100; i++){ assert(array[i] == i); } } //----------------------------------------------------------------------------- // String interning //----------------------------------------------------------------------------- typedef struct Table_Index{ U64 hash; U64 index; U64 iter; U64 max_size; }Table_Index; typedef struct Intern_String{ String s; }Intern_String; typedef struct Intern_Table{ S64 interns_in_bytes; S64 interns_inserted; S64 interns_max; Intern_String *interns; Arena *arena; }Intern_Table; function Intern_Table intern_table(Arena *arena, SizeU size){ Intern_Table result = {0}; result.arena = arena; result.interns = arena_push_array(arena, Intern_String, size); result.interns_max = size; return result; } function Table_Index table_index_from_hash(U64 hash, U64 max_size){ Table_Index result = {0}; result.hash = hash; result.index = result.hash % max_size; result.iter = result.index; result.max_size = max_size; return result; } function Table_Index table_index_from_string(String string, U64 max_size){ U64 hash = hash_fnv(string); Table_Index result = table_index_from_hash(hash, max_size); return result; } function B32 table_index_advance(Table_Index *index){ index->iter = wrap_around_pow2(index->iter + 1, index->max_size); B32 result = index->iter == index->index; return result; } function Intern_String intern_string(Intern_Table *p, String string){ Intern_String result = {0}; Table_Index index = table_index_from_string(string, p->interns_max); for(;;){ Intern_String *intern = p->interns + index.iter; if(intern->s.str == 0){ result.s = arena_push_string_copy(p->arena, string); p->interns_in_bytes += string.len; p->interns_inserted += 1; *intern = result; break; } else if(string_compare(intern->s, string)){ result = *intern; break; } if (table_index_advance(&index)) break; } return result; } function B32 intern_compare(Intern_String a, Intern_String b){ B32 result = a.s.str == b.s.str; return result; } function void intern_test(){ Arena *scratch = arena_begin_scratch(); Intern_Table table = intern_table(scratch, 512); assert(intern_compare(intern_string(&table, lit("Thing")), intern_string(&table, lit("Thing")))); assert(!intern_compare(intern_string(&table, lit("Thing")), intern_string(&table, lit("No_Thing")))); assert(intern_compare(intern_string(&table, lit("No_Thing")), intern_string(&table, lit("No_Thing")))); }