added compile.ini and moved some files in deprecated (still need some work)

This commit is contained in:
snarmph 2023-02-06 11:34:12 +01:00
parent 87751fad5f
commit 8fdecd89a5
5 changed files with 525 additions and 517 deletions

8
compile.ini Normal file
View file

@ -0,0 +1,8 @@
include = colla/
files = colla/*.c
[windows]
link = ws2_32.lib
[linux]
link = pthread

View file

@ -1,322 +1,322 @@
#include "coropool.h" #include "coropool.h"
#if 0 #if 0
#include <stdlib.h> #include <stdlib.h>
#include <tracelog.h> #include <tracelog.h>
#include "jobpool.h" #include "jobpool.h"
enum { enum {
NUM_JOBS = 50 NUM_JOBS = 50
}; };
struct _pool_internal_t; struct _pool_internal_t;
typedef union job_t { typedef union job_t {
struct { struct {
mco_coro *co; mco_coro *co;
struct _pool_internal_t *pool; struct _pool_internal_t *pool;
}; };
struct { struct {
union job_t *next_free; union job_t *next_free;
void *__padding; void *__padding;
}; };
} job_t; } job_t;
static inline bool _isJobValid(job_t *job) { static inline bool _isJobValid(job_t *job) {
return job->pool != NULL; return job->pool != NULL;
} }
typedef struct jobchunk_t { typedef struct jobchunk_t {
job_t jobs[NUM_JOBS]; job_t jobs[NUM_JOBS];
job_t *first_free; job_t *first_free;
struct jobchunk_t *next; struct jobchunk_t *next;
} jobchunk_t; } jobchunk_t;
void _chunkInit(jobchunk_t *chunk) { void _chunkInit(jobchunk_t *chunk) {
if (!chunk) return; if (!chunk) return;
chunk->first_free = chunk->jobs; chunk->first_free = chunk->jobs;
for (int i = 0; i < (NUM_JOBS - 1); ++i) { for (int i = 0; i < (NUM_JOBS - 1); ++i) {
chunk->jobs[i].next_free = chunk->jobs + i + 1; chunk->jobs[i].next_free = chunk->jobs + i + 1;
} }
} }
jobchunk_t *_chunkNew(jobchunk_t *prev) { jobchunk_t *_chunkNew(jobchunk_t *prev) {
jobchunk_t *chunk = calloc(1, sizeof(jobchunk_t)); jobchunk_t *chunk = calloc(1, sizeof(jobchunk_t));
_chunkInit(chunk); _chunkInit(chunk);
prev->next = chunk; prev->next = chunk;
return chunk; return chunk;
} }
job_t *_chunkGetFirstFree(jobchunk_t *chunk) { job_t *_chunkGetFirstFree(jobchunk_t *chunk) {
job_t *first_free = chunk->first_free; job_t *first_free = chunk->first_free;
if (first_free) { if (first_free) {
chunk->first_free = first_free->next_free; chunk->first_free = first_free->next_free;
} }
return first_free; return first_free;
} }
void _chunkSetFree(jobchunk_t *chunk, job_t *job) { void _chunkSetFree(jobchunk_t *chunk, job_t *job) {
job->pool = NULL; job->pool = NULL;
job->next_free = chunk->first_free; job->next_free = chunk->first_free;
chunk->first_free = job; chunk->first_free = job;
} }
typedef struct _pool_internal_t { typedef struct _pool_internal_t {
jobpool_t pool; jobpool_t pool;
jobchunk_t head_chunk; jobchunk_t head_chunk;
cmutex_t chunk_mtx; cmutex_t chunk_mtx;
} _pool_internal_t; } _pool_internal_t;
static int _worker(void *arg); static int _worker(void *arg);
coropool_t copoInit(uint32 num) { coropool_t copoInit(uint32 num) {
_pool_internal_t *pool = calloc(1, sizeof(_pool_internal_t)); _pool_internal_t *pool = calloc(1, sizeof(_pool_internal_t));
pool->pool = poolInit(num); pool->pool = poolInit(num);
_chunkInit(&pool->head_chunk); _chunkInit(&pool->head_chunk);
pool->chunk_mtx = mtxInit(); pool->chunk_mtx = mtxInit();
return pool; return pool;
} }
void copoFree(coropool_t copo) { void copoFree(coropool_t copo) {
_pool_internal_t *pool = copo; _pool_internal_t *pool = copo;
poolWait(pool->pool); poolWait(pool->pool);
poolFree(pool->pool); poolFree(pool->pool);
jobchunk_t *chunk = &pool->head_chunk; jobchunk_t *chunk = &pool->head_chunk;
while (chunk) { while (chunk) {
jobchunk_t *next = chunk->next; jobchunk_t *next = chunk->next;
free(chunk); free(chunk);
chunk = next; chunk = next;
} }
mtxFree(pool->chunk_mtx); mtxFree(pool->chunk_mtx);
free(pool); free(pool);
} }
bool copoAdd(coropool_t copo, copo_func_f fn) { bool copoAdd(coropool_t copo, copo_func_f fn) {
mco_desc desc = mco_desc_init(fn, 0); mco_desc desc = mco_desc_init(fn, 0);
return copoAddDesc(copo, &desc); return copoAddDesc(copo, &desc);
} }
bool copoAddDesc(coropool_t copo, mco_desc *desc) { bool copoAddDesc(coropool_t copo, mco_desc *desc) {
mco_coro *co; mco_coro *co;
mco_create(&co, desc); mco_create(&co, desc);
return copoAddCo(copo, co); return copoAddCo(copo, co);
} }
static bool _copoAddJob(job_t *job) { static bool _copoAddJob(job_t *job) {
//return poolAdd(job->pool->pool, _worker, job); //return poolAdd(job->pool->pool, _worker, job);
return true; return true;
} }
static bool _copoRemoveJob(job_t *job) { static bool _copoRemoveJob(job_t *job) {
_pool_internal_t *pool = job->pool; _pool_internal_t *pool = job->pool;
// find chunk // find chunk
jobchunk_t *chunk = &pool->head_chunk; jobchunk_t *chunk = &pool->head_chunk;
while (chunk) { while (chunk) {
if (chunk->jobs < job && (chunk->jobs + NUM_JOBS) > job) { if (chunk->jobs < job && (chunk->jobs + NUM_JOBS) > job) {
break; break;
} }
chunk = chunk->next; chunk = chunk->next;
} }
if (!chunk) return false; if (!chunk) return false;
mtxLock(pool->chunk_mtx); mtxLock(pool->chunk_mtx);
_chunkSetFree(chunk, job); _chunkSetFree(chunk, job);
mtxUnlock(pool->chunk_mtx); mtxUnlock(pool->chunk_mtx);
} }
bool copoAddCo(coropool_t copo, mco_coro *co) { bool copoAddCo(coropool_t copo, mco_coro *co) {
_pool_internal_t *pool = copo; _pool_internal_t *pool = copo;
job_t *new_job = NULL; job_t *new_job = NULL;
jobchunk_t *chunk = &pool->head_chunk; jobchunk_t *chunk = &pool->head_chunk;
mtxLock(pool->chunk_mtx); mtxLock(pool->chunk_mtx);
while (!new_job) { while (!new_job) {
new_job = _chunkGetFirstFree(chunk); new_job = _chunkGetFirstFree(chunk);
if (!new_job) { if (!new_job) {
if (!chunk->next) { if (!chunk->next) {
info("adding new chunk"); info("adding new chunk");
chunk->next = _chunkNew(chunk); chunk->next = _chunkNew(chunk);
} }
chunk = chunk->next; chunk = chunk->next;
} }
} }
mtxUnlock(pool->chunk_mtx); mtxUnlock(pool->chunk_mtx);
new_job->co = co; new_job->co = co;
new_job->pool = pool; new_job->pool = pool;
//return poolAdd(pool->pool, _worker, new_job); //return poolAdd(pool->pool, _worker, new_job);
return _copoAddJob(new_job); return _copoAddJob(new_job);
} }
void copoWait(coropool_t copo) { void copoWait(coropool_t copo) {
_pool_internal_t *pool = copo; _pool_internal_t *pool = copo;
poolWait(pool->pool); poolWait(pool->pool);
} }
static int _worker(void *arg) { static int _worker(void *arg) {
job_t *job = arg; job_t *job = arg;
mco_result res = mco_resume(job->co); mco_result res = mco_resume(job->co);
if (res != MCO_DEAD) { if (res != MCO_DEAD) {
_copoAddJob(job); _copoAddJob(job);
} }
else { else {
_copoRemoveJob(job); _copoRemoveJob(job);
} }
return 0; return 0;
} }
#endif #endif
#include <vec.h> #include <vec.h>
typedef struct { typedef struct {
vec(mco_coro *) jobs; vec(mco_coro *) jobs;
uint32 head; uint32 head;
cmutex_t work_mutex; cmutex_t work_mutex;
condvar_t work_cond; condvar_t work_cond;
condvar_t working_cond; condvar_t working_cond;
int32 working_count; int32 working_count;
int32 thread_count; int32 thread_count;
bool stop; bool stop;
} _copo_internal_t; } _copo_internal_t;
static mco_coro *_getJob(_copo_internal_t *copo); static mco_coro *_getJob(_copo_internal_t *copo);
static int _copoWorker(void *arg); static int _copoWorker(void *arg);
coropool_t copoInit(uint32 num) { coropool_t copoInit(uint32 num) {
if (!num) num = 2; if (!num) num = 2;
_copo_internal_t *copo = malloc(sizeof(_copo_internal_t)); _copo_internal_t *copo = malloc(sizeof(_copo_internal_t));
*copo = (_copo_internal_t){ *copo = (_copo_internal_t){
.work_mutex = mtxInit(), .work_mutex = mtxInit(),
.work_cond = condInit(), .work_cond = condInit(),
.working_cond = condInit(), .working_cond = condInit(),
.thread_count = (int32)num .thread_count = (int32)num
}; };
for (usize i = 0; i < num; ++i) { for (usize i = 0; i < num; ++i) {
thrDetach(thrCreate(_copoWorker, copo)); thrDetach(thrCreate(_copoWorker, copo));
} }
return copo; return copo;
} }
void copoFree(coropool_t copo_in) { void copoFree(coropool_t copo_in) {
_copo_internal_t *copo = copo_in; _copo_internal_t *copo = copo_in;
if (!copo) return; if (!copo) return;
mtxLock(copo->work_mutex); mtxLock(copo->work_mutex);
copo->stop = true; copo->stop = true;
condWakeAll(copo->work_cond); condWakeAll(copo->work_cond);
mtxUnlock(copo->work_mutex); mtxUnlock(copo->work_mutex);
copoWait(copo); copoWait(copo);
vecFree(copo->jobs); vecFree(copo->jobs);
mtxFree(copo->work_mutex); mtxFree(copo->work_mutex);
condFree(copo->work_cond); condFree(copo->work_cond);
condFree(copo->working_cond); condFree(copo->working_cond);
free(copo); free(copo);
} }
bool copoAdd(coropool_t copo, copo_func_f fn) { bool copoAdd(coropool_t copo, copo_func_f fn) {
mco_desc desc = mco_desc_init(fn, 0); mco_desc desc = mco_desc_init(fn, 0);
return copoAddDesc(copo, &desc); return copoAddDesc(copo, &desc);
} }
bool copoAddDesc(coropool_t copo, mco_desc *desc) { bool copoAddDesc(coropool_t copo, mco_desc *desc) {
mco_coro *co; mco_coro *co;
mco_create(&co, desc); mco_create(&co, desc);
return copoAddCo(copo, co); return copoAddCo(copo, co);
} }
bool copoAddCo(coropool_t copo_in, mco_coro *co) { bool copoAddCo(coropool_t copo_in, mco_coro *co) {
_copo_internal_t *copo = copo_in; _copo_internal_t *copo = copo_in;
if (!copo) return false; if (!copo) return false;
mtxLock(copo->work_mutex); mtxLock(copo->work_mutex);
if (copo->head > vecLen(copo->jobs)) { if (copo->head > vecLen(copo->jobs)) {
vecClear(copo->jobs); vecClear(copo->jobs);
copo->head = 0; copo->head = 0;
} }
vecAppend(copo->jobs, co); vecAppend(copo->jobs, co);
condWake(copo->work_cond); condWake(copo->work_cond);
mtxUnlock(copo->work_mutex); mtxUnlock(copo->work_mutex);
return true; return true;
} }
void copoWait(coropool_t copo_in) { void copoWait(coropool_t copo_in) {
_copo_internal_t *copo = copo_in; _copo_internal_t *copo = copo_in;
if (!copo) return; if (!copo) return;
mtxLock(copo->work_mutex); mtxLock(copo->work_mutex);
// while its either // while its either
// - working and there's still some threads doing some work // - working and there's still some threads doing some work
// - not working and there's still some threads exiting // - not working and there's still some threads exiting
while ((!copo->stop && copo->working_count > 0) || while ((!copo->stop && copo->working_count > 0) ||
(copo->stop && copo->thread_count > 0) (copo->stop && copo->thread_count > 0)
) { ) {
condWait(copo->working_cond, copo->work_mutex); condWait(copo->working_cond, copo->work_mutex);
} }
mtxUnlock(copo->work_mutex); mtxUnlock(copo->work_mutex);
} }
// == PRIVATE FUNCTIONS =================================== // == PRIVATE FUNCTIONS ===================================
static mco_coro *_getJob(_copo_internal_t *copo) { static mco_coro *_getJob(_copo_internal_t *copo) {
if (copo->head >= vecLen(copo->jobs)) { if (copo->head >= vecLen(copo->jobs)) {
copo->head = 0; copo->head = 0;
} }
return copo->jobs[copo->head++]; return copo->jobs[copo->head++];
} }
static int _copoWorker(void *arg) { static int _copoWorker(void *arg) {
_copo_internal_t *copo = arg; _copo_internal_t *copo = arg;
while (true) { while (true) {
mtxLock(copo->work_mutex); mtxLock(copo->work_mutex);
// wait for a new job // wait for a new job
while (copo->head >= vecLen(copo->jobs) && !copo->stop) { while (copo->head >= vecLen(copo->jobs) && !copo->stop) {
condWait(copo->work_cond, copo->work_mutex); condWait(copo->work_cond, copo->work_mutex);
} }
if (copo->stop) { if (copo->stop) {
break; break;
} }
mco_coro *job = _getJob(copo); mco_coro *job = _getJob(copo);
copo->working_count++; copo->working_count++;
mtxUnlock(copo->work_mutex); mtxUnlock(copo->work_mutex);
if (job && mco_status(job) != MCO_DEAD) { if (job && mco_status(job) != MCO_DEAD) {
mco_resume(job); mco_resume(job);
if (mco_status(job) != MCO_DEAD) { if (mco_status(job) != MCO_DEAD) {
copoAddCo(copo, job); copoAddCo(copo, job);
} }
} }
mtxLock(copo->work_mutex); mtxLock(copo->work_mutex);
copo->working_count--; copo->working_count--;
if (!copo->stop && if (!copo->stop &&
copo->working_count == 0 && copo->working_count == 0 &&
copo->head == vecLen(copo->jobs) copo->head == vecLen(copo->jobs)
) { ) {
condWake(copo->working_cond); condWake(copo->working_cond);
} }
mtxUnlock(copo->work_mutex); mtxUnlock(copo->work_mutex);
} }
copo->thread_count--; copo->thread_count--;
condWake(copo->working_cond); condWake(copo->working_cond);
mtxUnlock(copo->work_mutex); mtxUnlock(copo->work_mutex);
return 0; return 0;
} }

View file

@ -1,17 +1,17 @@
#pragma once #pragma once
#include <collatypes.h> #include <collatypes.h>
#include <cthreads.h> #include <cthreads.h>
#include "minicoro.h" #include "minicoro.h"
typedef void *coropool_t; typedef void *coropool_t;
typedef void (*copo_func_f)(mco_coro *co); typedef void (*copo_func_f)(mco_coro *co);
coropool_t copoInit(uint32 num); coropool_t copoInit(uint32 num);
void copoFree(coropool_t copo); void copoFree(coropool_t copo);
bool copoAdd(coropool_t copo, copo_func_f fn); bool copoAdd(coropool_t copo, copo_func_f fn);
bool copoAddDesc(coropool_t copo, mco_desc *desc); bool copoAddDesc(coropool_t copo, mco_desc *desc);
bool copoAddCo(coropool_t copo, mco_coro *co); bool copoAddCo(coropool_t copo, mco_coro *co);
void copoWait(coropool_t copo); void copoWait(coropool_t copo);

View file

@ -1,130 +1,130 @@
#include "hashmap.h" #include "hashmap.h"
#include <string.h> #include <string.h>
static uint64 hash_seed = 0; static uint64 hash_seed = 0;
hashmap_t hmInit(usize initial_cap) { hashmap_t hmInit(usize initial_cap) {
hashmap_t map = {0}; hashmap_t map = {0};
if (!initial_cap) initial_cap = 512; if (!initial_cap) initial_cap = 512;
vecReserve(map.nodes, initial_cap); vecReserve(map.nodes, initial_cap);
memset(map.nodes, 0, sizeof(hashnode_t) * initial_cap); memset(map.nodes, 0, sizeof(hashnode_t) * initial_cap);
return map; return map;
} }
void hmFree(hashmap_t map) { void hmFree(hashmap_t map) {
vecFree(map.nodes); vecFree(map.nodes);
} }
void hmSet(hashmap_t *map, uint64 hash, uint64 index) { void hmSet(hashmap_t *map, uint64 hash, uint64 index) {
uint32 hm_index = hash % vecCap(map->nodes); uint32 hm_index = hash % vecCap(map->nodes);
while (map->nodes[hm_index].hash) { while (map->nodes[hm_index].hash) {
hashnode_t *node = &map->nodes[hm_index]; hashnode_t *node = &map->nodes[hm_index];
if (node->hash == hash) { if (node->hash == hash) {
node->index = index; node->index = index;
return; return;
} }
hm_index = (hm_index + 1) % vecCap(map->nodes); hm_index = (hm_index + 1) % vecCap(map->nodes);
} }
map->nodes[hm_index].hash = hash; map->nodes[hm_index].hash = hash;
map->nodes[hm_index].index = index; map->nodes[hm_index].index = index;
_veclen(map->nodes)++; _veclen(map->nodes)++;
float load_factor = (float)vecLen(map->nodes) / (float)vecCap(map->nodes); float load_factor = (float)vecLen(map->nodes) / (float)vecCap(map->nodes);
if (load_factor > 0.75f) { if (load_factor > 0.75f) {
uint32 old_cap = vecCap(map->nodes); uint32 old_cap = vecCap(map->nodes);
vecReserve(map->nodes, old_cap); vecReserve(map->nodes, old_cap);
for (usize i = old_cap; i < vecCap(map->nodes); ++i) { for (usize i = old_cap; i < vecCap(map->nodes); ++i) {
map->nodes[i].hash = 0; map->nodes[i].hash = 0;
map->nodes[i].index = 0; map->nodes[i].index = 0;
} }
} }
} }
uint64 hmGet(hashmap_t map, uint64 hash) { uint64 hmGet(hashmap_t map, uint64 hash) {
uint32 hm_index = hash % vecCap(map.nodes); uint32 hm_index = hash % vecCap(map.nodes);
do { do {
hashnode_t *node = &map.nodes[hm_index]; hashnode_t *node = &map.nodes[hm_index];
if (node->hash == hash) { if (node->hash == hash) {
return node->index; return node->index;
} }
hm_index = (hm_index + 1) % vecCap(map.nodes); hm_index = (hm_index + 1) % vecCap(map.nodes);
} while (map.nodes[hm_index].hash); } while (map.nodes[hm_index].hash);
return 0; return 0;
} }
void hmDelete(hashmap_t *map, uint64 hash) { void hmDelete(hashmap_t *map, uint64 hash) {
uint32 hm_index = hash % vecCap(map->nodes); uint32 hm_index = hash % vecCap(map->nodes);
do { do {
hashnode_t *node = &map->nodes[hm_index]; hashnode_t *node = &map->nodes[hm_index];
if (node->hash == hash) { if (node->hash == hash) {
node->hash = 0; node->hash = 0;
node->index = 0; node->index = 0;
break; break;
} }
hm_index = (hm_index + 1) % vecCap(map->nodes); hm_index = (hm_index + 1) % vecCap(map->nodes);
} while (map->nodes[hm_index].hash); } while (map->nodes[hm_index].hash);
if(vecLen(map->nodes)) _veclen(map->nodes)--; if(vecLen(map->nodes)) _veclen(map->nodes)--;
} }
void hashSetSeed(uint64 new_seed) { void hashSetSeed(uint64 new_seed) {
hash_seed = new_seed; hash_seed = new_seed;
} }
uint64 hash(const void *ptr, usize len) { uint64 hash(const void *ptr, usize len) {
const uint64 m = 0xC6A4A7935BD1E995LLU; const uint64 m = 0xC6A4A7935BD1E995LLU;
const int r = 47; const int r = 47;
uint64 h = hash_seed ^ (len * m); uint64 h = hash_seed ^ (len * m);
const uint64 *data = (const uint64 *)ptr; const uint64 *data = (const uint64 *)ptr;
const uint64 *end = (len >> 3) + data; const uint64 *end = (len >> 3) + data;
while (data != end) { while (data != end) {
uint64 k = *data++; uint64 k = *data++;
k *= m; k *= m;
k ^= k >> r; k ^= k >> r;
k *= m; k *= m;
h ^= k; h ^= k;
h *= m; h *= m;
} }
const unsigned char * data2 = (const unsigned char *)data; const unsigned char * data2 = (const unsigned char *)data;
switch(len & 7) { switch(len & 7) {
case 7: h ^= (uint64_t)(data2[6]) << 48; case 7: h ^= (uint64_t)(data2[6]) << 48;
case 6: h ^= (uint64_t)(data2[5]) << 40; case 6: h ^= (uint64_t)(data2[5]) << 40;
case 5: h ^= (uint64_t)(data2[4]) << 32; case 5: h ^= (uint64_t)(data2[4]) << 32;
case 4: h ^= (uint64_t)(data2[3]) << 24; case 4: h ^= (uint64_t)(data2[3]) << 24;
case 3: h ^= (uint64_t)(data2[2]) << 16; case 3: h ^= (uint64_t)(data2[2]) << 16;
case 2: h ^= (uint64_t)(data2[1]) << 8; case 2: h ^= (uint64_t)(data2[1]) << 8;
case 1: h ^= (uint64_t)(data2[0]); case 1: h ^= (uint64_t)(data2[0]);
h *= m; h *= m;
}; };
h ^= h >> r; h ^= h >> r;
h *= m; h *= m;
h ^= h >> r; h ^= h >> r;
return h; return h;
} }
uint64 hashStr(str_t str) { uint64 hashStr(str_t str) {
return hash(str.buf, str.len); return hash(str.buf, str.len);
} }
uint64 hashView(strview_t view) { uint64 hashView(strview_t view) {
return hash(view.buf, view.len); return hash(view.buf, view.len);
} }
uint64 hashCStr(const char *cstr) { uint64 hashCStr(const char *cstr) {
return hash(cstr, strlen(cstr)); return hash(cstr, strlen(cstr));
} }

View file

@ -1,49 +1,49 @@
#pragma once #pragma once
#include "collatypes.h" #include "collatypes.h"
#include "vec.h" #include "vec.h"
#include "str.h" #include "str.h"
/* /*
Example usage: Example usage:
hashSetSeed(time(NULL)); hashSetSeed(time(NULL));
vec(const char *) strings = NULL; vec(const char *) strings = NULL;
hashmap_t map = hmInit(32); hashmap_t map = hmInit(32);
// mapGet returns 0 in case it doesn't find anything, this way we don't need // mapGet returns 0 in case it doesn't find anything, this way we don't need
// to check its return value // to check its return value
vecAppend(strings, "nil"); vecAppend(strings, "nil");
hmSet(&map, hashCStr("english"), vecAppend(strings, "hello")); hmSet(&map, hashCStr("english"), vecAppend(strings, "hello"));
hmSet(&map, hashCStr("french"), vecAppend(strings, "bonjour")); hmSet(&map, hashCStr("french"), vecAppend(strings, "bonjour"));
hmSet(&map, hashCStr("italian"), vecAppend(strings, "ciao")); hmSet(&map, hashCStr("italian"), vecAppend(strings, "ciao"));
printf("english: %s\n", strings[hmGet(map, hashCStr("english"))]); printf("english: %s\n", strings[hmGet(map, hashCStr("english"))]);
printf("french: %s\n", strings[hmGet(map, hashCStr("french"))]); printf("french: %s\n", strings[hmGet(map, hashCStr("french"))]);
printf("italian: %s\n", strings[hmGet(map, hashCStr("italian"))]); printf("italian: %s\n", strings[hmGet(map, hashCStr("italian"))]);
mapFree(map); mapFree(map);
vecFree(strings); vecFree(strings);
*/ */
typedef struct { typedef struct {
uint64 hash; uint64 hash;
uint64 index; uint64 index;
} hashnode_t; } hashnode_t;
typedef struct { typedef struct {
vec(hashnode_t) nodes; vec(hashnode_t) nodes;
} hashmap_t; } hashmap_t;
hashmap_t hmInit(usize initial_cap); hashmap_t hmInit(usize initial_cap);
void hmFree(hashmap_t map); void hmFree(hashmap_t map);
void hmSet(hashmap_t *map, uint64 hash, uint64 index); void hmSet(hashmap_t *map, uint64 hash, uint64 index);
uint64 hmGet(hashmap_t map, uint64 hash); uint64 hmGet(hashmap_t map, uint64 hash);
void hmDelete(hashmap_t *map, uint64 hash); void hmDelete(hashmap_t *map, uint64 hash);
void hashSetSeed(uint64 new_seed); void hashSetSeed(uint64 new_seed);
uint64 hash(const void *data, usize len); uint64 hash(const void *data, usize len);
uint64 hashStr(str_t str); uint64 hashStr(str_t str);
uint64 hashView(strview_t view); uint64 hashView(strview_t view);
uint64 hashCStr(const char *cstr); uint64 hashCStr(const char *cstr);