//////////////////////////////// //~ Hash types #define Fnv64Basis 0xCBF29CE484222325 //////////////////////////////// //~ Mergesort types /* Compare functions should * return a positive value if a should go before b * return a negative value if a should go after b * return 0 otherwise */ #define MergesortCompareFuncDef(name, arg_a, arg_b, arg_udata) i32 name(void *arg_a, void *arg_b, void *arg_udata) typedef MergesortCompareFuncDef(MergesortCompareFunc, a, b, udata); //////////////////////////////// //~ Dict types Struct(DictEntry) { u64 hash; u64 value; DictEntry *prev_in_bin; DictEntry *next_in_bin; DictEntry *prev; DictEntry *next; }; Struct(DictBin) { DictEntry *first; DictEntry *last; }; Struct(Dict) { u64 bins_count; DictBin *bins; DictEntry *first_free; DictEntry *first; DictEntry *last; }; //////////////////////////////// //~ Hash utils /* FNV-1a parameters for different hash sizes: * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters */ Inline u64 HashFnv64(u64 seed, String s) { u64 hash = seed; for (u64 i = 0; i < s.len; ++i) { hash ^= (u8)s.text[i]; hash *= 0x100000001B3; } return hash; } //////////////////////////////// //~ Mergesort utils Inline void MergesortInternal(u8 *left, u8 *right, u8 *items, u64 left_count, u64 right_count, u64 item_size, MergesortCompareFunc *callback, void *udata) { /* Sort */ u64 i = 0; u64 l = 0; u64 r = 0; while (l < left_count && r < right_count) { u8 *dst = items + (i * item_size); u8 *left_item = left + (l * item_size); u8 *right_item = right + (r * item_size); ++i; if (callback(left_item, right_item, udata) > 0) { CopyBytes(dst, left_item, item_size); ++l; } else { CopyBytes(dst, right_item, item_size); ++r; } } /* Copy remaining */ if (l != left_count) { u64 remaining_count = left_count - l; u64 remaining_bytes = remaining_count * item_size; u8 *dst = items + (i * item_size); u8 *src = left + (l * item_size); CopyBytes(dst, src, remaining_bytes); } else if (r != right_count) { u64 remaining_count = right_count - r; u64 remaining_bytes = remaining_count * item_size; u8 *dst = items + (i * item_size); u8 *src = right + (r * item_size); CopyBytes(dst, src, remaining_bytes); } } Inline void Mergesort(void *items, u64 item_count, u64 item_size, MergesortCompareFunc *callback, void *udata) { if (item_count > 1) { TempArena scratch = BeginScratchNoConflict(); u64 left_count = item_count / 2; u64 right_count = item_count - left_count; u64 left_size = left_count * item_size; u64 right_size = right_count * item_size; u8 *left = PushStructsNoZero(scratch.arena, u8, left_size); u8 *right = PushStructsNoZero(scratch.arena, u8, right_size); CopyBytes(left, items, left_size); CopyBytes(right, (u8 *)items + left_size, right_size); Mergesort(left, left_count, item_size, callback, udata); Mergesort(right, right_count, item_size, callback, udata); MergesortInternal(left, right, (u8 *)items, left_count, right_count, item_size, callback, udata); EndScratch(scratch); } } //////////////////////////////// //~ Dict utils //- Dict init Inline Dict *InitDict(Arena *arena, u64 bins_count) { __prof; Dict *dict = PushStruct(arena, Dict); dict->bins_count = MaxU64(bins_count, 1); /* Ensure at least 1 bin */ dict->bins = PushStructs(arena, DictBin, dict->bins_count); return dict; } Inline void ResetDict(Dict *dict) { ZeroBytes(dict->bins, sizeof(*dict->bins) * dict->bins_count); if (dict->first) { dict->last->next = dict->first_free; dict->first_free = dict->first; } } //- Dict set Inline DictEntry *EnsureDictEntry(Arena *arena, Dict *dict, u64 hash) { __prof; DictBin *bin = &dict->bins[hash % dict->bins_count]; DictEntry *entry = bin->first; while (entry) { if (hash == entry->hash) { /* Existing match found */ break; } entry = entry->next_in_bin; } /* No match found, create new entry */ if (!entry) { if (dict->first_free) { entry = dict->first_free; dict->first_free = entry->next; } else { entry = PushStructNoZero(arena, DictEntry); } ZeroStruct(entry); entry->hash = hash; if (bin->last) { bin->last->next_in_bin = entry; entry->prev_in_bin = bin->last; } else { bin->first = entry; } bin->last = entry; if (dict->last) { dict->last->next = entry; entry->prev = dict->last; } else { dict->first = entry; } dict->last = entry; } return entry; } Inline void SetDictValue(Arena *arena, Dict *dict, u64 hash, u64 value) { __prof; DictEntry *entry = EnsureDictEntry(arena, dict, hash); entry->value = value; } //- Dict remove Inline void RemoveDictEntry(Dict *dict, DictEntry *entry) { /* Remove from bin */ { DictBin *bin = &dict->bins[entry->hash % dict->bins_count]; DictEntry *prev_in_bin = entry->prev_in_bin; DictEntry *next_in_bin = entry->next_in_bin; if (prev_in_bin) { prev_in_bin->next_in_bin = next_in_bin; } else { bin->first = next_in_bin; } if (next_in_bin) { next_in_bin->prev_in_bin = prev_in_bin; } else { bin->last = prev_in_bin; } } /* Remove from list */ { DictEntry *prev = entry->prev; DictEntry *next = entry->next; if (prev) { prev->next = next; } else { dict->first = next; } if (next) { next->prev = prev; } else { dict->last = prev; } } /* Insert into free list */ { entry->next = dict->first_free; dict->first_free = entry; } } //- Dict index Inline DictEntry *DictEntryFromHash(Dict *dict, u64 hash) { __prof; DictEntry *result = 0; DictBin *bin = &dict->bins[hash % dict->bins_count]; for (DictEntry *entry = bin->first; entry; entry = entry->next_in_bin) { if (hash == entry->hash) { /* Match found */ result = entry; break; } } return result; } Inline u64 DictValueFromHash(Dict *dict, u64 hash) { __prof; DictEntry *entry = DictEntryFromHash(dict, hash); return entry ? entry->value : 0; }