function S64 clamp_top_s64(S64 val, S64 max){ if(val>max)return max; return val; } function SizeU clamp_top_sizeu(SizeU val, SizeU max){ if(val>max)return max; return val; } function SizeU get_align_offset(SizeU size, SizeU align){ SizeU mask = align - 1; SizeU val = size & mask; if(val){ val = align - val; } return val; } function SizeU align_up(SizeU size, SizeU align){ SizeU result = size + get_align_offset(size, align); return result; } 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 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; } //----------------------------------------------------------------------------- // 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")))); }