267 lines
5.9 KiB
C
267 lines
5.9 KiB
C
////////////////////////////////////////////////////////////
|
|
//~ Mergesort types
|
|
|
|
// Comparison 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;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ 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
|
|
|
|
Inline Dict *InitDict(Arena *arena, u64 bins_count)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
Inline DictEntry *EnsureDictEntry(Arena *arena, Dict *dict, u64 hash)
|
|
{
|
|
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)
|
|
{
|
|
DictEntry *entry = EnsureDictEntry(arena, dict, hash);
|
|
entry->value = value;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
// Add to free list
|
|
{
|
|
entry->next = dict->first_free;
|
|
dict->first_free = entry;
|
|
}
|
|
}
|
|
|
|
Inline DictEntry *DictEntryFromHash(Dict *dict, u64 hash)
|
|
{
|
|
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)
|
|
{
|
|
DictEntry *entry = DictEntryFromHash(dict, hash);
|
|
return entry ? entry->value : 0;
|
|
}
|
|
|
|
Inline u64 DictValueOrNilFromHash(Dict *dict, u64 hash, u64 nil)
|
|
{
|
|
DictEntry *entry = DictEntryFromHash(dict, hash);
|
|
return entry ? entry->value : nil;
|
|
}
|