391 lines
11 KiB
C
391 lines
11 KiB
C
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// sort/less
|
||
|
|
||
|
int sort_64(const void *a, const void *b) {
|
||
|
return 0[(uint64_t*)a] - 0[(uint64_t*)b];
|
||
|
}
|
||
|
|
||
|
int less_int(int a, int b) {
|
||
|
return a - b;
|
||
|
}
|
||
|
int less_64(uint64_t a, uint64_t b) {
|
||
|
return a > b ? +1 : -!!(a - b);
|
||
|
}
|
||
|
int less_ptr(void *a, void *b) {
|
||
|
return (uintptr_t)a > (uintptr_t)b ? +1 : -!!((uintptr_t)a - (uintptr_t)b);
|
||
|
}
|
||
|
int less_str(char *a, char *b) {
|
||
|
return strcmp((const char *)a, (const char *)b);
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// un/hash
|
||
|
|
||
|
uint32_t unhash_32(uint32_t x) {
|
||
|
// Thomas Mueller at https://stackoverflow.com/questions/664014/ - says no collisions for 32bits!
|
||
|
x = ((x >> 16) ^ x) * 0x119de1f3;
|
||
|
x = ((x >> 16) ^ x) * 0x119de1f3;
|
||
|
x = (x >> 16) ^ x;
|
||
|
return x;
|
||
|
}
|
||
|
uint32_t hash_32(uint32_t x) {
|
||
|
// Thomas Mueller at https://stackoverflow.com/questions/664014/ - says no collisions for 32bits!
|
||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||
|
x = (x >> 16) ^ x;
|
||
|
return x;
|
||
|
}
|
||
|
uint64_t hash_64(uint64_t x) {
|
||
|
#if 1
|
||
|
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
|
||
|
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
|
||
|
x = x ^ (x >> 31);
|
||
|
return x;
|
||
|
#else
|
||
|
// should we just use x2 hash_32?
|
||
|
uint32_t hi = (x >> 32ull), lo = (x & ~0u);
|
||
|
return (hash_32(hi) << 32ull) | hash_32(lo);
|
||
|
#endif
|
||
|
}
|
||
|
uint64_t hash_flt(double x) {
|
||
|
union { double d; uint64_t i; } c;
|
||
|
return c.d = x, hash_64(c.i);
|
||
|
}
|
||
|
uint64_t hash_str(const char* str) {
|
||
|
uint64_t hash = 14695981039346656037ULL; // hash(0),mul(131) faster than fnv1a, a few more collisions though
|
||
|
while( *str ) hash = ( (unsigned char)*str++ ^ hash ) * 0x100000001b3ULL;
|
||
|
return hash;
|
||
|
}
|
||
|
uint64_t hash_int(int key) {
|
||
|
return hash_32((uint32_t)key);
|
||
|
}
|
||
|
uint64_t hash_ptr(const void *ptr) {
|
||
|
uint64_t key = (uint64_t)(uintptr_t)ptr;
|
||
|
return hash_64(key); // >> 3? needed?
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// utils
|
||
|
|
||
|
uint64_t popcnt64(uint64_t x) {
|
||
|
// [src] https://en.wikipedia.org/wiki/Hamming_weight
|
||
|
x -= (x >> 1) & 0x5555555555555555ULL; //put count of each 2 bits into those 2 bits
|
||
|
x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); //put count of each 4 bits into those 4 bits
|
||
|
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; //put count of each 8 bits into those 8 bits
|
||
|
return (x * 0x0101010101010101ULL) >> 56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// vector based allocator (x1.75 enlarge factor)
|
||
|
|
||
|
void* vrealloc( void* p, size_t sz ) {
|
||
|
if( !sz ) {
|
||
|
if( p ) {
|
||
|
size_t *ret = (size_t*)p - 2;
|
||
|
ret[0] = 0;
|
||
|
ret[1] = 0;
|
||
|
REALLOC( ret, 0 );
|
||
|
}
|
||
|
return 0;
|
||
|
} else {
|
||
|
size_t *ret;
|
||
|
if( !p ) {
|
||
|
ret = (size_t*)REALLOC( 0, sizeof(size_t) * 2 + sz );
|
||
|
ret[0] = sz;
|
||
|
ret[1] = 0;
|
||
|
} else {
|
||
|
ret = (size_t*)p - 2;
|
||
|
size_t osz = ret[0];
|
||
|
size_t ocp = ret[1];
|
||
|
if( sz <= (osz + ocp) ) {
|
||
|
ret[0] = sz;
|
||
|
ret[1] = ocp - (sz - osz);
|
||
|
} else {
|
||
|
ret = (size_t*)REALLOC( ret, sizeof(size_t) * 2 + sz * 1.75 );
|
||
|
ret[0] = sz;
|
||
|
ret[1] = (size_t)(sz * 1.75) - sz;
|
||
|
}
|
||
|
}
|
||
|
return &ret[2];
|
||
|
}
|
||
|
}
|
||
|
size_t vlen( void* p ) {
|
||
|
return p ? 0[ (size_t*)p - 2 ] : 0;
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
enum { MAP_GC_SLOT = MAP_HASHSIZE };
|
||
|
typedef int map_is_pow2_assert[ !(MAP_HASHSIZE & (MAP_HASHSIZE - 1)) ? 1:-1];
|
||
|
|
||
|
static int map_get_index(uint64_t hkey1) {
|
||
|
return hkey1 & (MAP_HASHSIZE-1);
|
||
|
}
|
||
|
|
||
|
void (map_init)(map* m) {
|
||
|
map c = {0};
|
||
|
*m = c;
|
||
|
|
||
|
array_resize(m->array, (MAP_HASHSIZE+1));
|
||
|
memset(m->array, 0, (MAP_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset()
|
||
|
}
|
||
|
|
||
|
void (map_insert)(map* m, pair *p, void *key, void *value, uint64_t keyhash, void *super) {
|
||
|
p->keyhash = keyhash;
|
||
|
p->key = key;
|
||
|
p->value = value;
|
||
|
p->super = super;
|
||
|
|
||
|
/* Insert onto the beginning of the list */
|
||
|
int index = map_get_index(p->keyhash);
|
||
|
p->next = m->array[index];
|
||
|
m->array[index] = p;
|
||
|
m->is_sorted = 0;
|
||
|
|
||
|
++m->count;
|
||
|
}
|
||
|
|
||
|
void* (map_find)(map* m, void *key, uint64_t keyhash) {
|
||
|
int index = map_get_index(keyhash);
|
||
|
for( pair *cur = m->array[index]; cur; cur = cur->next ) {
|
||
|
if( cur->keyhash == keyhash ) {
|
||
|
char **c = (char **)cur->key;
|
||
|
char **k = (char **)key;
|
||
|
if( !m->cmp(c[0], k[0]) ) {
|
||
|
return cur->super;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void (map_erase)(map* m, void *key, uint64_t keyhash) {
|
||
|
int index = map_get_index(keyhash);
|
||
|
for( pair *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) {
|
||
|
if( cur->keyhash == keyhash ) {
|
||
|
char **c = (char **)cur->key;
|
||
|
char **k = (char **)key;
|
||
|
if( !m->cmp(c[0], k[0]) ) {
|
||
|
if( prev ) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0;
|
||
|
#if MAP_DONT_ERASE
|
||
|
/* Insert onto the beginning of the GC list */
|
||
|
cur->next = m->array[MAP_GC_SLOT];
|
||
|
m->array[MAP_GC_SLOT] = cur;
|
||
|
#else
|
||
|
REALLOC(cur,0);
|
||
|
#endif
|
||
|
--m->count;
|
||
|
m->is_sorted = 0;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int (map_count)(map* m) {
|
||
|
return m->count;
|
||
|
int counter = 0;
|
||
|
for( int i = 0; i < MAP_HASHSIZE; ++i) {
|
||
|
for( pair *cur = m->array[i]; cur; cur = cur->next ) {
|
||
|
++counter;
|
||
|
}
|
||
|
}
|
||
|
return counter;
|
||
|
}
|
||
|
|
||
|
void (map_gc)(map* m) {
|
||
|
#if MAP_DONT_ERASE
|
||
|
for( pair *next, *cur = m->array[MAP_GC_SLOT]; cur; cur = next ) {
|
||
|
next = cur->next;
|
||
|
REALLOC(cur,0);
|
||
|
}
|
||
|
m->array[MAP_GC_SLOT] = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void (map_clear)(map* m) {
|
||
|
for( int i = 0; i <= MAP_HASHSIZE; ++i) {
|
||
|
for( pair *next, *cur = m->array[i]; cur; cur = next ) {
|
||
|
next = cur->next;
|
||
|
REALLOC(cur,0);
|
||
|
}
|
||
|
m->array[i] = 0;
|
||
|
}
|
||
|
m->count = 0;
|
||
|
m->is_sorted = 0;
|
||
|
}
|
||
|
|
||
|
bool (map_sort)(map* m) {
|
||
|
if( m->is_sorted ) return false;
|
||
|
|
||
|
array_clear(m->sorted);
|
||
|
// array_reserve(m->sorted, m->count);
|
||
|
|
||
|
for( int i = 0; i < array_count(m->array); ++i) {
|
||
|
for( pair *cur = m->array[i]; cur; cur = cur->next ) {
|
||
|
array_push(m->sorted, cur);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
array_sort(m->sorted, m->cmp);
|
||
|
#else
|
||
|
// @fixme: do better than bubble sort below
|
||
|
for( int i = 0; i < array_count(m->sorted) - 1; ++i)
|
||
|
for( int j = i+1; j < array_count(m->sorted); ++j) {
|
||
|
pair *curi = m->sorted[i];
|
||
|
pair *curj = m->sorted[j];
|
||
|
char **c = (char **)curi->key;
|
||
|
char **k = (char **)curj->key;
|
||
|
if( m->cmp(c[0], k[0]) > 0 ) {
|
||
|
pair *swap = m->sorted[i];
|
||
|
m->sorted[i] = m->sorted[j];
|
||
|
m->sorted[j] = swap;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return m->is_sorted = true;
|
||
|
}
|
||
|
|
||
|
void (map_free)(map* m) {
|
||
|
(map_clear)(m);
|
||
|
|
||
|
array_free(m->array);
|
||
|
m->array = 0;
|
||
|
|
||
|
map c = {0};
|
||
|
*m = c;
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
enum { set_GC_SLOT = SET_HASHSIZE };
|
||
|
typedef int set_is_pow2_assert[ !(SET_HASHSIZE & (SET_HASHSIZE - 1)) ? 1:-1];
|
||
|
|
||
|
static int set_get_index(uint64_t hkey1) {
|
||
|
return hkey1 & (SET_HASHSIZE-1);
|
||
|
}
|
||
|
|
||
|
void (set_init)(set* m) {
|
||
|
set zero = {0};
|
||
|
*m = zero;
|
||
|
|
||
|
array_resize(m->array, (SET_HASHSIZE+1));
|
||
|
memset(m->array, 0, (SET_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset()
|
||
|
}
|
||
|
|
||
|
void (set_insert)(set* m, set_item *p, void *key, uint64_t keyhash, void *super) {
|
||
|
p->keyhash = keyhash;
|
||
|
p->key = key;
|
||
|
p->super = super;
|
||
|
|
||
|
/* Insert onto the beginning of the list */
|
||
|
int index = set_get_index(p->keyhash);
|
||
|
p->next = m->array[index];
|
||
|
m->array[index] = p;
|
||
|
|
||
|
++m->count;
|
||
|
}
|
||
|
|
||
|
void* (set_find)(const set* m, void *key, uint64_t keyhash) {
|
||
|
int index = set_get_index(keyhash);
|
||
|
for( const set_item *cur = m->array[index]; cur; cur = cur->next ) {
|
||
|
if( cur->keyhash == keyhash ) {
|
||
|
char **c = (char **)cur->key;
|
||
|
char **k = (char **)key;
|
||
|
if( !m->cmp(c[0], k[0]) ) {
|
||
|
return cur->super;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void (set_erase)(set* m, void *key, uint64_t keyhash) {
|
||
|
int index = set_get_index(keyhash);
|
||
|
for( set_item *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) {
|
||
|
if( cur->keyhash == keyhash ) {
|
||
|
char **c = (char **)cur->key;
|
||
|
char **k = (char **)key;
|
||
|
if( !m->cmp(c[0], k[0]) ) {
|
||
|
if (prev) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0;
|
||
|
#if SET_DONT_ERASE
|
||
|
/* Insert onto the beginning of the GC list */
|
||
|
cur->next = m->array[set_GC_SLOT];
|
||
|
m->array[set_GC_SLOT] = cur;
|
||
|
#else
|
||
|
REALLOC(cur,0);
|
||
|
#endif
|
||
|
--m->count;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int (set_count)(const set* m) { // does not include GC_SLOT
|
||
|
return m->count;
|
||
|
int counter = 0;
|
||
|
for( int i = 0; i < SET_HASHSIZE; ++i) {
|
||
|
for( const set_item *cur = m->array[i]; cur; cur = cur->next ) {
|
||
|
++counter;
|
||
|
}
|
||
|
}
|
||
|
return counter;
|
||
|
}
|
||
|
|
||
|
void (set_gc)(set* m) { // clean deferred GC_SLOT only
|
||
|
#if SET_DONT_ERASE
|
||
|
for( set_item *next, *cur = m->array[set_GC_SLOT]; cur; cur = next ) {
|
||
|
next = cur->next;
|
||
|
REALLOC(cur,0);
|
||
|
}
|
||
|
m->array[set_GC_SLOT] = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void (set_clear)(set* m) { // include GC_SLOT
|
||
|
for( int i = 0; i <= SET_HASHSIZE; ++i) {
|
||
|
for( set_item *next, *cur = m->array[i]; cur; cur = next ) {
|
||
|
next = cur->next;
|
||
|
REALLOC(cur,0);
|
||
|
}
|
||
|
m->array[i] = 0;
|
||
|
}
|
||
|
m->count = 0;
|
||
|
}
|
||
|
|
||
|
void (set_free)(set* m) {
|
||
|
(set_clear)(m);
|
||
|
|
||
|
array_free(m->array);
|
||
|
m->array = 0;
|
||
|
|
||
|
set zero = {0};
|
||
|
*m = zero;
|
||
|
}
|
||
|
|
||
|
char *cc4str(unsigned x) {
|
||
|
static __thread char type[4+1] = {0};
|
||
|
type[3] = (x >> 24ULL) & 255;
|
||
|
type[2] = (x >> 16ULL) & 255;
|
||
|
type[1] = (x >> 8ULL) & 255;
|
||
|
type[0] = (x >> 0ULL) & 255;
|
||
|
return type;
|
||
|
}
|
||
|
char *cc8str(uint64_t x) {
|
||
|
static __thread char type[8+1] = {0};
|
||
|
type[7] = (x >> 56ULL) & 255;
|
||
|
type[6] = (x >> 48ULL) & 255;
|
||
|
type[5] = (x >> 40ULL) & 255;
|
||
|
type[4] = (x >> 32ULL) & 255;
|
||
|
type[3] = (x >> 24ULL) & 255;
|
||
|
type[2] = (x >> 16ULL) & 255;
|
||
|
type[1] = (x >> 8ULL) & 255;
|
||
|
type[0] = (x >> 0ULL) & 255;
|
||
|
return type;
|
||
|
}
|
||
|
|