Lazyc/lazy.h
2026-07-29 00:50:44 -04:00

597 lines
14 KiB
C

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#ifndef LZ_STR_FMT
#define LZ_STR_FMT "%.*s"
#endif
#ifndef LZ_STR_PRINTF
#define LZ_STR_PRINTF(slc) (int)slc.cnt, slc.data
#endif
#ifndef LZ_SLC_DEF
#define LZ_SLC_DEF
typedef struct {
char *data;
size_t cnt;
} Lz_Slc;
#endif
#ifndef LZ_SB_DEF
#define LZ_SB_DEF
typedef struct {
char *data;
size_t cnt;
size_t cap;
} Lz_SB;
#endif
#ifndef LZ_ALLOC_DATA_DEF
#define LZ_ALLOC_DATA_DEF
typedef struct {
size_t size;
void *ptr;
char file[256];
int line;
} Lz_Alloc_Data;
#endif
void lz_slc_trim_left_typ(Lz_Slc *src, int (*classfunc)(int));
void lz_slc_trim_right_typ(Lz_Slc *src, int (*classfunc)(int));
void lz_slc_trim_typ(Lz_Slc *src, int (*classfunc)(int));
void lz_slc_drop_left(Lz_Slc *src, size_t n);
void lz_slc_drop_right(Lz_Slc *src, size_t n);
void lz_slc_split(Lz_Slc *src, char delim, Lz_Slc *dst);
void lz_slc_split_typ(Lz_Slc *src, int (*classfunc)(int), Lz_Slc *dst);
size_t lz_hm_hash_raw_bytes(size_t cap, void *key, size_t k_s);
size_t lz_hm_hash_void_get(size_t cap, _Bool *exists, _Bool *tombstones,
void *key, void *keys, size_t k_s);
size_t lz_hm_hash_void_set(size_t cap, _Bool *exists,
_Bool *tombstones, void* key, size_t k_s);
Lz_Slc lz_slc_from_cstr(char *dat);
char *lz_sb_to_cstr(Lz_SB *sb);
void lz_sb_append_cstr(Lz_SB *sb, const char *cstr);
void lz_sb_add_char(Lz_SB *sb, char c);
char *lz_cstr_dup(const char *s);
double lz_slc_atof(Lz_Slc *src);
int lz_slc_atoi(Lz_Slc *src);
long lz_slc_atol(Lz_Slc *src);
long long lz_slc_atoll(Lz_Slc *src);
#ifndef Lz_DA
#define Lz_DA(T) struct {T *data; size_t cnt; size_t cap; }
#endif
#ifndef lz_da_append
#define lz_da_append(da, x) do { \
if ((da).cap > (da).cnt) {\
(da).data[(da).cnt++] = (x);\
} else if((da).cap == 0) {\
(da).data = malloc(256*sizeof(*(da).data));\
(da).cap = 256;\
(da).cnt = 0;\
(da).data[(da).cnt++] = (x);\
} else {\
(da).data = realloc((da).data, sizeof(*(da).data)*(da).cap*(2));\
(da).cap *= 2;\
(da).data[(da).cnt++] = (x);\
}\
} while(0)
#endif
#ifndef lz_da_insert
#define lz_da_insert(da, i, x) do {\
if ((i) > (da).cnt) {\
assert(0 && "Insert out of dynamic array size");\
}\
if ((i) < 0) {\
assert(0 && "Negative index of insertion");\
}\
if ((da).cap > (da).cnt) {\
memmove((da).data+(i)+1,(da).data+(i),((da).cnt-(i))*sizeof(*(da).data));\
(da).data[i] = (x);\
(da).cnt++;\
} else if ((da).cap == (da).cnt) {\
(da).data = realloc((da).data, sizeof(*(da).data)*(da).cap*2);\
(da).cap *= 2;\
memmove((da).data+(i)+1,(da).data+(i),((da).cnt-(i))*sizeof(*(da).data));\
(da).data[i] = (x);\
(da).cnt++;\
} else {\
assert(0 && "Unreachable thru proper dynamic array functions");\
}\
} while(0)
#endif
#ifndef lz_da_pop
#define lz_da_pop(da) ((da).data[--(da).cnt])
#endif
#ifndef lz_da_concat
#define lz_da_concat(dst,src) do {\
if (sizeof(*(dst).data) != sizeof(*(dst).data)) {\
assert(0 && "Mismatched types of arrays being concatenated");\
}\
lz_da_reserve((dst),((src).cnt+(dst).cnt));\
memcpy((dst).data+(dst).cnt, (src).data, (src).cnt*sizeof(*(dst).data));\
(dst).cnt = ((src).cnt+(dst).cnt);\
} while(0)
#endif
#ifndef lz_da_remove
#define lz_da_remove(da, idx) do {\
if ((da).cnt <= (idx)) {\
assert(0 && "Removal index out of bounds");\
}\
--(da).cnt;\
memmove((da).data+idx,(da).data+idx+1,((da).cnt-idx)*sizeof(*(da.data)));\
} while(0)
#endif
#ifndef lz_da_destroy
#define lz_da_destroy(da) do { free((da).data); } while (0);
#endif
#ifndef lz_da_reserve
#define lz_da_reserve(da, size) do {\
if ((da).cap < (size)) {\
(da).data = realloc((da).data, (size)*sizeof(*(da).data));\
(da).cap = (size);\
}\
} while (0)
#endif
#ifndef lz_da_shrink_to_fit
#define lz_da_shrink_to_fit(da) do {\
if ((da).cnt == (da).cap) break;\
(da).data = realloc((da).data, (da).cnt * sizeof(*(da).data));\
(da).cap = (da).cnt;\
} while(0)
#endif
#ifndef lz_da_foreach
#define lz_da_foreach(T, name, da) \
for (T *name = (da).data; name-(da).data < (da).cnt; ++name)
#endif
#ifndef lz_da_fill_from_array
#define lz_da_fill_from_array(p_da, arr) do {\
free((p_da)->data);\
size_t cnt = sizeof(arr)/sizeof(*arr);\
(p_da)->cap = cnt;\
(p_da)->cnt = cnt;\
(p_da)->data = malloc(sizeof(arr));\
memcpy((p_da)->data, arr, sizeof(arr));\
} while(0)
#endif
double lz_sb_atof(Lz_SB *src);
int lz_sb_atoi(Lz_SB *src);
long lz_sb_atol(Lz_SB *src);
long long lz_sb_atoll(Lz_SB *src);
#ifndef Lz_HM
#define Lz_HM(K_t, V_t) struct {\
size_t cap;\
size_t cnt;\
size_t tmphash;\
V_t defval;\
K_t tmpkey;\
V_t tmpval;\
_Bool *exists;\
_Bool *tombstones;\
K_t *keys;\
V_t *vals;\
}
#endif
#ifndef lz_hm_init
#define lz_hm_init(hm) do {\
(hm).cap = 256;\
(hm).cnt = 0;\
(hm).keys = malloc((hm).cap*sizeof(*(hm).keys));\
(hm).vals = malloc((hm).cap*sizeof(*(hm).vals));\
(hm).exists = calloc((hm).cap,sizeof(_Bool));\
(hm).tombstones = calloc((hm).cap,sizeof(_Bool));\
} while (0)
#endif
#ifndef lz_hm_hash_set
#define lz_hm_hash_set(hm, k) (\
(hm).tmpkey = k,\
lz_hm_hash_void_set((hm).cap, (hm).exists, (hm).tombstones,\
&(hm).tmpkey, sizeof(*(hm).keys))\
)
#endif
#ifndef lz_hm_hash_get
#define lz_hm_hash_get(hm, k) (\
(hm).tmpkey = k,\
lz_hm_hash_void_get((hm).cap, (hm).exists, (hm).tombstones,\
&(hm).tmpkey, (hm).keys, sizeof(*(hm).keys))\
)
#endif
#ifndef lz_hm_exists
#define lz_hm_exists(hm, k) (\
(hm).tmphash = lz_hm_hash_get(hm, k),\
((hm).tmphash != (size_t)-1)\
)
#endif
#ifndef lz_hm_get
#define lz_hm_get(hm, k) (\
(hm).tmphash = lz_hm_hash_get(hm, k),\
((hm).tmphash == (size_t)-1) ? (hm).defval : ((hm).vals[(hm).tmphash])\
)
#endif
#ifndef lz_hm_grow
#define lz_hm_grow(hm) do {\
size_t newcap = (hm).cap*2;\
void *newkeys = malloc(newcap*sizeof(*(hm).keys));\
void *newvals = malloc(newcap*sizeof(*(hm).vals));\
_Bool *newexists = calloc(newcap, sizeof(_Bool));\
_Bool *newtombstones = calloc(newcap, sizeof(_Bool));\
\
for (size_t i = 0; i < (hm).cap; ++i) {\
if ((hm).exists[i]) {\
size_t newhash = lz_hm_hash_void_set(newcap, newexists,\
newtombstones,\
&(hm).keys[i],\
sizeof(*(hm).keys));\
memcpy(newvals + sizeof(*(hm).vals)*newhash,\
&(hm).vals[i], sizeof(*(hm).vals));\
memcpy(newkeys + sizeof(*(hm).keys)*newhash,\
&(hm).keys[i], sizeof(*(hm).keys));\
newexists[newhash] = 1;\
newtombstones[newhash] = 0;\
}\
}\
free((hm).vals);\
(hm).vals = newvals;\
free((hm).keys);\
(hm).keys = newkeys;\
free((hm).exists);\
(hm).exists = newexists;\
free((hm).tombstones);\
(hm).tombstones = newtombstones;\
(hm).cap = newcap;\
} while(0)
#endif
#ifndef lz_hm_add
#define lz_hm_add(hm, k, v) do {\
size_t idx = lz_hm_hash_set(hm, k);\
(hm).keys[idx] = k;\
(hm).vals[idx] = v;\
++(hm).cnt;\
(hm).exists[idx] = 1;\
(hm).tombstones[idx] = 0;\
if (4*(hm).cnt > 3*(hm).cap) {\
lz_hm_grow(hm);\
}\
} while(0)
#endif
#ifndef lz_hm_remove
#define lz_hm_remove(hm, k) do {\
size_t idx = lz_hm_hash_get(hm,k);\
(hm).tombstones[idx] = 1;\
(hm).exists[idx] = 0;\
} while(0)
#endif
#ifndef lz_hm_destroy
#define lz_hm_destroy(hm, k) do {\
free((hm).keys);\
free((hm).vals);\
free((hm).exists);\
free((hm).tombstones);\
} while(0)
#endif
#ifdef LAZY_IMPL
#undef LAZY_IMPL
Lz_DA(Lz_Alloc_Data) _lz_allocations = {0};
size_t lz_hm_hash_raw_bytes(size_t cap, void *key, size_t k_s) {
size_t hash = 0;
for (size_t i = 0; i < k_s; ++i) {
size_t b = (size_t)(((uint8_t*)key)[i]);
hash += b*31415 + 9265; //bytewise hash
hash %= cap;
}
return hash % cap;
}
size_t lz_hm_hash_void_get(size_t cap, _Bool *exists, _Bool *tombstones,
void *key, void *keys, size_t k_s) {
size_t hash = lz_hm_hash_raw_bytes(cap, key, k_s);
for (size_t i = 0; i < cap; ++i) {
size_t localhash = (hash + i) % cap;
int diff = memcmp(keys+k_s*localhash, key, k_s);
size_t tombstone = tombstones[localhash];
if (diff == 0 && !tombstone) {
return localhash;
}
size_t istaken = exists[localhash];
if (!istaken && !tombstone) {
return (size_t)-1;
}
}
return (size_t)-1;
}
size_t lz_hm_hash_void_set(size_t cap, _Bool *exists,
_Bool *tombstones, void* key, size_t k_s) {
size_t hash = lz_hm_hash_raw_bytes(cap, key, k_s);
for (size_t i = 0; i < cap; ++i) {
size_t localhash = (hash + i) % cap;
_Bool tombstone = tombstones[localhash];
_Bool istaken = exists[localhash];
if (!istaken || tombstone) return localhash;
}
return (size_t)-1;
}
// Horribly enough, strdup is a gnu extension
// Here's a portable implementation
char *lz_cstr_dup(const char *s) {
if (!s) return NULL;
size_t n = strlen(s);
char *p = malloc((n+1)*sizeof(char));
if (!p) return NULL;
memcpy(p, s, n+1);
return p;
}
void lz_slc_drop_left(Lz_Slc *src, size_t n) {
if (src->cnt < n) n = src->cnt;
src->data += n;
src->cnt -= n;
}
void lz_slc_drop_right(Lz_Slc *src, size_t n) {
if (src->cnt < n) n = src->cnt;
src->cnt -= n;
}
void lz_slc_trim_left_typ(Lz_Slc *src, int (*classfunc)(int)) {
while (classfunc(src->data[0])) {
lz_slc_drop_left(src, 1);
}
}
void lz_slc_trim_right_typ(Lz_Slc *src, int (*classfunc)(int)) {
while (classfunc(src->data[src->cnt-1])) {
lz_slc_drop_right(src, 1);
}
}
void lz_slc_trim_typ(Lz_Slc *src, int (*classfunc)(int)) {
lz_slc_trim_left_typ(src, classfunc);
lz_slc_trim_right_typ(src, classfunc);
}
void lz_slc_split(Lz_Slc *src, char delim, Lz_Slc *dst) {
size_t i = 0;
while (i < src->cnt && src->data[i] != delim) {
++i;
}
dst->data = src->data;
dst->cnt = i;
if (src->cnt != i) {
src->data += i+1;
src->cnt -= i+1;
} else {
src->cnt = 0;
}
}
void lz_slc_split_typ(Lz_Slc *src, int (*classfunc)(int), Lz_Slc *dst) {
size_t i = 0;
while (i < src->cnt && !classfunc(src->data[i])) {
++i;
}
dst->data = src->data;
dst->cnt = i;
if (src->cnt != i) {
src->data += i+1;
src->cnt -= i+1;
} else {
src->cnt = 0;
}
lz_slc_trim_left_typ(src, classfunc);
}
Lz_SB lz_sb_from_cstr(const char *dat) {
size_t len = strlen(dat);
Lz_SB sb = {0};
lz_da_reserve(sb, len);
memcpy(sb.data, dat, len*sizeof(char));
sb.cnt = len;
return sb;
}
char *lz_sb_to_cstr(Lz_SB *sb) {
char *cstr = malloc(sb->cnt+1);
memcpy(cstr, sb->data, sizeof(char)*sb->cnt);
cstr[sb->cnt] = 0;
return cstr;
}
#define lz_da_append(da, x) do { \
if ((da).cap > (da).cnt) {\
(da).data[(da).cnt++] = (x);\
} else if((da).cap == 0) {\
(da).data = malloc(256*sizeof(*(da).data));\
(da).cap = 256;\
(da).cnt = 0;\
(da).data[(da).cnt++] = (x);\
} else {\
(da).data = realloc((da).data, sizeof(*(da).data)*(da).cap*(2));\
(da).cap *= 2;\
(da).data[(da).cnt++] = (x);\
}\
} while(0)
void lz_sb_add_char(Lz_SB *sb, char c) {
if (sb->cap > sb->cnt) {
sb->data[sb->cnt++] = c;
} else if(sb->cap == 0) {
sb->data = malloc(256*sizeof(*sb->data));
sb->cap = 256;
sb->cnt = 0;
sb->data[sb->cnt++] = c;
} else {
sb->data = realloc(sb->data, sizeof(*sb->data)*sb->cap*2);
sb->cap *= 2;
sb->data[sb->cnt++] = c;
}
}
void lz_sb_append_cstr(Lz_SB *sb, const char *cstr) {
while (*cstr) {
lz_sb_add_char(sb, *cstr);
++cstr;
}
}
Lz_Slc lz_slc_from_cstr(char *dat) {
Lz_Slc out = {0};
out.data = dat;
out.cnt = strlen(dat);
return out;
}
double lz_slc_atof(Lz_Slc *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
double x = atof(cstr);
free(cstr);
return x;
}
int lz_slc_atoi(Lz_Slc *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
int x = atoi(cstr);
free(cstr);
return x;
}
long lz_slc_atol(Lz_Slc *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
int x = atol(cstr);
free(cstr);
return x;
}
long long lz_slc_atoll(Lz_Slc *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
int x = atoll(cstr);
free(cstr);
return x;
}
double lz_sb_atof(Lz_SB *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
double x = atof(cstr);
free(cstr);
return x;
}
int lz_sb_atoi(Lz_SB *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
int x = atoi(cstr);
free(cstr);
return x;
}
long lz_sb_atol(Lz_SB *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
int x = atol(cstr);
free(cstr);
return x;
}
long long lz_sb_atoll(Lz_SB *src) {
char *cstr = malloc(src->cnt+1);
memcpy(cstr, src->data, src->cnt);
cstr[src->cnt] = 0;
int x = atoll(cstr);
free(cstr);
return x;
}
void *lz_tracking_malloc(size_t size, const char* file, int line) {
void *ptr = malloc(size);
Lz_Alloc_Data dat = (Lz_Alloc_Data){
.size = size,
.ptr = ptr,
.line = line
};
strncpy(dat.file, file, 256);
lz_da_append(_lz_allocations, dat);
return ptr;
}
void lz_tracking_free(void *ptr, const char *file, int line) {
for (int i = 0; i < _lz_allocations.cnt; ++i) {
if (_lz_allocations.data[i].ptr == ptr) {
lz_da_remove(_lz_allocations, i);
free(ptr);
return;
}
}
printf("[LZ_MEM_ANALYZER] Freeing unallocated memory @ %x, %s, line %d",
ptr, file, line);
exit(1);
}
void lz_mem_analyzer_debug(const char *file, int line) {
printf("[LZ_MEM_ANALYZER] Memory report from %s, line %d\n", file, line);
if (_lz_allocations.cnt == 0) {
printf("All tests passed :)\n");
return;
}
printf("Found %d unfreed chunks of memory\n", _lz_allocations.cnt);
for (int i = 0; i < _lz_allocations.cnt; ++i) {
Lz_Alloc_Data dat = _lz_allocations.data[i];
printf("Non-freed memory found: %x\n", dat.ptr);
printf(" size: %d bytes\n", dat.size);
printf(" allocated at:\n");
printf(" %s, line %d\n", dat.file, dat.line);
}
}
#endif
#ifdef LZ_MEM_ANALYZER
#undef LZ_MEM_ANALYZER
// TODO: implement the other memory functions (calloc and some others)
#define malloc(size) lz_tracking_malloc(size, __FILE__, __LINE__)
#define free(ptr) lz_tracking_free(ptr, __FILE__, __LINE__)
#define lz_mem_debug() lz_mem_analyzer_debug(__FILE__, __LINE__)
#else
#define lz_mem_debug()
#endif