Use macro templates instead of void pointers

This commit is contained in:
jun 2025-01-04 19:47:15 +01:00
parent 0751726fc5
commit 9e0da64cb4
15 changed files with 694 additions and 1066 deletions

View File

@ -2,10 +2,10 @@ cmake_minimum_required(VERSION 3.12)
set(PROJECT_NAME libguf) set(PROJECT_NAME libguf)
project(${PROJECT_NAME}) project(${PROJECT_NAME})
set(SOURCES src/guf_common.c src/guf_str.c src/guf_dict.c src/guf_dbuf.c src/guf_obj.c) set(SOURCES src/guf_str.c src/guf_dict.c)
add_library(${PROJECT_NAME} STATIC ${SOURCES}) add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf) # target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf)
# target_include_directories(${PROJECT_NAME} PRIVATE src) # target_include_directories(${PROJECT_NAME} PRIVATE src)
add_executable(libguf_test ${SOURCES} src/guf_test.c) add_executable(libguf_test ${SOURCES} src/guf_test.c)

View File

@ -1,3 +1,15 @@
#ifndef GUF_ASSERT_H
#define GUF_ASSERT_H
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h>
#define GUF_ASSERT(COND) assert(COND)
#define GUF_ASSERT_RELEASE(COND) do { \
if (!(COND)) { \
fprintf(stderr, "libguf release assertion failed: " #COND ", file " __FILE__ ", line %d\n", __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0);
#endif

View File

@ -1,177 +0,0 @@
#include "guf_common.h"
#include <stdlib.h>
#include <stdarg.h>
#include "guf_dict.h"
#include "guf_str.h"
bool guf_is_big_endian(void)
{
unsigned i = 1;
const char *bytes = (const char*)&i;
return bytes[0] != 1;
}
// typedef struct alloc_info {
// size_t num_alloc, num_free;
// } alloc_info;
// static bool init = false;
// static guf_dict alloc_table;
// static guf_dict pointer_cnt;
// bool guf_alloc_init(void)
// {
// alloc_table = GUF_DICT_UNINITIALISED;
// pointer_cnt = GUF_DICT_UNINITIALISED;
// guf_dict_kv_funcs alloc_info_funcs = GUF_DICT_FUNCS_NULL;
// alloc_info_funcs.type_size = sizeof(alloc_info);
// bool success = guf_dict_init(&alloc_table, 64, &GUF_DICT_FUNCS_GUF_STR, &alloc_info_funcs);
// if (!success) {
// return false;
// }
// guf_dict_kv_funcs void_ptr_funcs = GUF_DICT_FUNCS_NULL;
// void_ptr_funcs.type_size = sizeof(void*);
// guf_dict_kv_funcs size_t_funcs = GUF_DICT_FUNCS_NULL;
// size_t_funcs.type_size = sizeof(size_t);
// success = guf_dict_init(&pointer_cnt, 128, &void_ptr_funcs, &size_t_funcs);
// if (!success) {
// return false;
// }
// if (success) {
// init = true;
// }
// return success;
// }
// static void track_alloc(void *ptr, const char *name)
// {
// if (!init) {
// return;
// }
// if (guf_dict_contains_key(&pointer_cnt, &ptr)) {
// GUF_ASSERT_RELEASE(false);
// } else {
// size_t cnt = 1;
// bool succ = guf_dict_insert(&pointer_cnt, &ptr, &cnt, GUF_DICT_CPY_KEY_VAL);
// GUF_ASSERT_RELEASE(succ);
// }
// guf_str name_str = guf_str_new_view_from_cstr(name);
// if (guf_dict_contains_key(&alloc_table, &name_str)) {
// alloc_info *ai = guf_dict_get_val(&alloc_table, &name_str);
// GUF_ASSERT_RELEASE(ai);
// ai->num_alloc += 1;
// return;
// } else {
// guf_str new_str = guf_str_new(name);
// GUF_ASSERT_RELEASE(guf_str_is_valid(&new_str));
// alloc_info ai = {.num_alloc = 1, .num_free = 0};
// bool succ = guf_dict_insert(&alloc_table, &new_str, &ai, 0);
// GUF_ASSERT_RELEASE(succ);
// GUF_ASSERT(guf_dict_contains_key(&alloc_table, &name_str));
// }
// }
// static void track_free(void *ptr, const char *name)
// {
// if (!init) {
// return;
// }
// GUF_ASSERT_RELEASE(guf_dict_contains_key(&pointer_cnt, &ptr));
// size_t *cnt = guf_dict_get_val(&pointer_cnt, &ptr);
// GUF_ASSERT_RELEASE(cnt);
// if (*cnt == 0) {
// fprintf(stderr, "Double free for %s\n", name);
// GUF_ASSERT_RELEASE(false);
// } else{
// GUF_ASSERT(*cnt == 1);
// *cnt = 0;
// }
// const guf_str name_str = guf_str_new_view_from_cstr(name);
// if (guf_dict_contains_key(&alloc_table, &name_str)) {
// alloc_info *ai = guf_dict_get_val(&alloc_table, &name_str);
// GUF_ASSERT_RELEASE(ai);
// GUF_ASSERT_RELEASE(ai->num_alloc > 0);
// ai->num_free += 1;
// }
// }
// void *guf_malloc(size_t size, const char *name)
// {
// void *ptr = malloc(size);
// if (!ptr) {
// return ptr;
// }
// track_alloc(ptr, name);
// return ptr;
// }
// void *guf_calloc(size_t count, size_t size, const char *name)
// {
// void *ptr = calloc(count, size);
// if (!ptr) {
// return ptr;
// }
// track_alloc(ptr, name);
// return ptr;
// }
// void *guf_realloc(void *ptr, size_t size, const char *name)
// {
// void *new_ptr = realloc(ptr, size);
// if (!ptr) {
// return new_ptr;
// }
// track_alloc(ptr, name);
// return new_ptr;
// }
// void guf_free(void *ptr, const char *name)
// {
// if (!ptr) {
// return;
// }
// track_free(ptr, name);
// free(ptr);
// return;
// }
// void guf_alloc_print(void)
// {
// if (!init) {
// printf("guf_alloc_print: guf_alloc not initialised\n");
// return;
// }
// printf("size: %zu\n", alloc_table.size);
// for (guf_dict_iter it = guf_dict_iter_begin(&alloc_table); !guf_dict_iter_is_end(&it); guf_dict_iter_advance(&it)) {
// const guf_str *key = it.key;
// alloc_info *val = it.val;
// // printf("idx: %zu elem %zu\n", it.idx, it.elems_seen);
// printf("'%s':\n - %zu alloc(s)\n - %zu free(s)\n\n", guf_str_get_const_c_str(key), val->num_alloc, val->num_free);
// }
// }

View File

@ -1,72 +1,26 @@
#ifndef GUF_COMMON_H #ifndef GUF_COMMON_H
#define GUF_COMMON_H #define GUF_COMMON_H
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "guf_common_utils.h"
#include "guf_hash.h"
#include "guf_assert.h" #include "guf_assert.h"
#define GUF_DICT_USE_32_BIT_HASH typedef enum guf_obj_cpy_opt {
GUF_CPY_VALUE = 0,
#ifdef GUF_DICT_USE_32_BIT_HASH GUF_CPY_DEEP = 1,
typedef uint32_t guf_hash_size_t; GUF_CPY_MOVE = 2,
#else } guf_obj_cpy_opt;
typedef uint64_t guf_hash_size_t;
#endif
#define GUF_ASSERT(COND) assert(COND)
#define GUF_ASSERT_RELEASE(COND) do { \
if (!(COND)) { \
fprintf(stderr, "libguf release assertion failed: " #COND ", file " __FILE__ ", line %d\n", __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0);
#define GUF_STATIC_BUF_SIZE(BUF) (sizeof((BUF)) / (sizeof((BUF)[0])))
#define GUF_ABS(X) ((X) >= 0 ? (X) : -(X))
#define GUF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
#define GUF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y))
#define GUF_CLAMP(X, MIN, MAX) GUF_MAX(GUF_MIN((X), (MAX)), (MIN))
static inline bool guf_is_mul_overflow_size_t(size_t a, size_t b)
{
size_t c = a * b;
return a != 0 && ((c / a) != b);
}
static inline size_t guf_safe_mul_size_t(size_t a, size_t b)
{
GUF_ASSERT_RELEASE(!guf_is_mul_overflow_size_t(a, b));
return a * b;
}
static inline bool guf_is_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem)
{
if (count < 0 || sizeof_elem <= 0) {
return false;
}
size_t size = guf_safe_mul_size_t(count, sizeof_elem);
return size <= PTRDIFF_MAX;
}
static inline ptrdiff_t guf_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem)
{
GUF_ASSERT_RELEASE(count >= 0);
GUF_ASSERT_RELEASE(sizeof_elem > 0);
size_t size = guf_safe_mul_size_t(count, sizeof_elem);
GUF_ASSERT_RELEASE(size <= PTRDIFF_MAX);
return size;
}
bool guf_is_big_endian(void);
bool guf_alloc_init(void);
void *guf_malloc(size_t size, const char *name);
void *guf_calloc(size_t count, size_t size, const char *name);
void *guf_realloc(void *ptr, size_t size, const char *name);
void guf_free(void *ptr, const char *name);
void guf_alloc_print(void);
// bool guf_alloc_init(void);
// void *guf_malloc(size_t size, const char *name);
// void *guf_calloc(size_t count, size_t size, const char *name);
// void *guf_realloc(void *ptr, size_t size, const char *name);
// void guf_free(void *ptr, const char *name);
// void guf_alloc_print(void);
#endif #endif

63
src/guf_common_utils.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef GUF_COMMON_UTILS_H
#define GUF_COMMON_UTILS_H
#include "guf_assert.h"
#include <limits.h>
// The GUFCAT/GUF_TOK_CAT indirection is necessary because the ## operation alone does not evaluate the macro arguments.
#define GUF_TOK_CAT(a, b) a##b
#define GUFCAT(a, b) GUF_TOK_CAT(a, b)
#define GUF_FOREACH(CNT_PTR, CNT_TYPE, IT_NAME) for (GUFCAT(CNT_TYPE, _iter) IT_NAME = GUFCAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.cur != IT_NAME.end; IT_NAME = IT_NAME.next(&IT_NAME, 1))
#define GUF_STATIC_BUF_SIZE(BUF) (sizeof((BUF)) / (sizeof((BUF)[0])))
#define GUF_ABS(X) ((X) >= 0 ? (X) : -(X))
#define GUF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
#define GUF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y))
#define GUF_CLAMP(X, MIN, MAX) GUF_MAX(GUF_MIN((X), (MAX)), (MIN))
static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN ); return -x;}
static inline long guf_abs_lng(long x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > LONG_MIN ); return -x;}
static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT8_MIN ); return -x;}
static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT16_MIN); return -x;}
static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT32_MIN); return -x;}
static inline int64_t guf_abs_i64(int64_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT64_MIN); return -x;}
static inline bool guf_is_mul_overflow_size_t(size_t a, size_t b)
{
size_t c = a * b;
return a != 0 && ((c / a) != b);
}
static inline size_t guf_safe_mul_size_t(size_t a, size_t b)
{
GUF_ASSERT_RELEASE(!guf_is_mul_overflow_size_t(a, b));
return a * b;
}
static inline bool guf_is_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem)
{
if (count < 0 || sizeof_elem <= 0) {
return false;
}
size_t size = guf_safe_mul_size_t(count, sizeof_elem);
return size <= PTRDIFF_MAX;
}
static inline ptrdiff_t guf_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem)
{
GUF_ASSERT_RELEASE(count >= 0);
GUF_ASSERT_RELEASE(sizeof_elem > 0);
size_t size = guf_safe_mul_size_t(count, sizeof_elem);
GUF_ASSERT_RELEASE(size <= PTRDIFF_MAX);
return size;
}
static inline bool guf_is_big_endian(void)
{
unsigned i = 1;
const char *bytes = (const char*)&i;
return bytes[0] != 1;
}
#endif

92
src/guf_cstr.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef GUF_CSTR_H
#define GUF_CSTR_H
#include <string.h>
#include "guf_common.h"
typedef const char* guf_const_cstr;
typedef char* guf_heap_cstr;
#define GUF_OBJ_TYPE guf_const_cstr
#include "guf_obj.h"
#define GUF_OBJ_TYPE guf_heap_cstr
#include "guf_obj.h"
static inline guf_heap_cstr *guf_heap_cstr_cpy_init(guf_heap_cstr *dst, const guf_heap_cstr *src)
{
GUF_ASSERT_RELEASE(dst && src);
if (*src == NULL) {
*dst = NULL;
return dst;
}
*dst = strdup(*src);
GUF_ASSERT_RELEASE(*dst);
return dst;
}
static inline guf_heap_cstr *guf_heap_cstr_move_init(guf_heap_cstr *dst, guf_heap_cstr *src)
{
GUF_ASSERT_RELEASE(dst && src);
*dst = *src;
*src = NULL;
return dst;
}
static inline void guf_heap_cstr_free(guf_heap_cstr *a)
{
GUF_ASSERT_RELEASE(a);
free(*a);
}
static inline int guf_heap_cstr_cmp(const guf_heap_cstr *a, const guf_heap_cstr *b)
{
GUF_ASSERT_RELEASE(a && b);
GUF_ASSERT_RELEASE(*a && *b);
return strcmp(*a, *b);
}
static inline GUF_OBJ_OPS_DEFINE_CMP_INVERSE(guf_heap_cstr, guf_heap_cstr_cmp, guf_heap_cstr_cmp_inv)
static inline bool guf_heap_cstr_eq(const guf_heap_cstr *a, const guf_heap_cstr *b)
{
GUF_ASSERT_RELEASE(a && b);
GUF_ASSERT_RELEASE(*a && *b);
return 0 == strcmp(*a, *b);
}
static guf_heap_cstr_ops guf_heap_cstr_operations = {
.copy_init = guf_heap_cstr_cpy_init,
.move_init = guf_heap_cstr_move_init,
.free = guf_heap_cstr_free,
.cmp = guf_heap_cstr_cmp,
.cmp_inv = guf_heap_cstr_cmp_inv,
.eq = guf_heap_cstr_eq
};
static inline int guf_const_cstr_cmp(const guf_const_cstr *a, const guf_const_cstr *b)
{
GUF_ASSERT_RELEASE(a && b);
GUF_ASSERT_RELEASE(*a && *b);
return strcmp(*a, *b);
}
static inline GUF_OBJ_OPS_DEFINE_CMP_INVERSE(guf_const_cstr, guf_const_cstr_cmp, guf_const_cstr_cmp_inv)
static inline bool guf_const_cstr_eq(const guf_const_cstr *a, const guf_const_cstr *b)
{
GUF_ASSERT_RELEASE(a && b);
GUF_ASSERT_RELEASE(*a && *b);
return 0 == strcmp(*a, *b);
}
static guf_const_cstr_ops guf_const_cstr_operations = {
.copy_init = NULL,
.move_init = NULL,
.free = NULL,
.cmp = guf_const_cstr_cmp,
.cmp_inv = guf_const_cstr_cmp_inv,
.eq = guf_const_cstr_eq
};
#endif

View File

@ -1,234 +0,0 @@
#ifndef GUF_DARR_H
#define GUF_DARR_H
#include <stddef.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "guf_assert.h"
#define GUF_DARR_NEW_CAPACITY(CAP) ((CAP) * 2)
#define GUF_DARR_FOREACH(ARR, ELEM_T, ELEM_PTR) assert((ARR).capacity); for (ELEM_T *ELEM_PTR = (ARR).data, *end = (ARR).data + (ARR).size; ELEM_PTR != end; ++ELEM_PTR)
// TODO: move and copy semantics? (TYPE vs pointer to TYPE); append_val vs append_ptr
// cpy makes only sense for ptrs or ref/handle types
#define GUF_DARR_DEFINE(TYPE, TYPENAME, ELEM_CPY, ELEM_FREE) \
typedef struct guf_darr_##TYPENAME { \
TYPE *data; \
size_t size, capacity; \
TYPE (*elem_cpy)(const TYPE elem); /* Can be NULL */ \
void (*elem_free)(TYPE elem); /* Can be NULL */ \
} guf_darr_##TYPENAME; \
\
bool guf_darr_##TYPENAME##_init(guf_darr_##TYPENAME *arr, size_t start_cap) { \
assert(arr); \
if (!arr) { \
return false; \
} \
if (start_cap < 1) { \
start_cap = 1; \
} \
const size_t buf_size = start_cap * sizeof(TYPE); \
if (buf_size < start_cap) { /* Overflow */ \
return false; \
} \
arr->data = malloc(buf_size); \
if (!arr->data) { \
arr->size = arr->capacity = 0; \
return false; \
} \
arr->size = 0; \
arr->capacity = start_cap; \
arr->elem_cpy = ELEM_CPY; \
arr->elem_free = ELEM_FREE; \
return true; \
}\
\
bool guf_darr_##TYPENAME##_append(guf_darr_##TYPENAME *arr, TYPE elem) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return false; \
}\
if (arr->size == arr->capacity) { \
const size_t new_cap = GUF_DARR_NEW_CAPACITY(arr->capacity); \
if (new_cap <= arr->capacity) { /* Overflow */ \
return false; \
} \
const size_t buf_size = new_cap * sizeof(TYPE); \
if (buf_size < new_cap) { /* Overflow */ \
return false; \
} \
TYPE *data_new = realloc(arr->data, buf_size); \
if (!data_new) { \
return false; \
} \
arr->data = data_new; \
arr->capacity = new_cap; \
} \
assert(arr->size < arr->capacity); \
if (arr->elem_cpy) { \
arr->data[arr->size++] = arr->elem_cpy(elem); \
} else { \
arr->data[arr->size++] = elem; \
} \
return true; \
}\
\
bool guf_darr_##TYPENAME##_insert_at(guf_darr_##TYPENAME *arr, TYPE elem, size_t idx) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return false; \
}\
assert(idx < arr->size); \
if (idx >= arr->size) { \
return false; \
} \
assert(arr->size != 0); \
if (arr->size == arr->capacity) { \
const size_t new_cap = GUF_DARR_NEW_CAPACITY(arr->capacity); \
if (new_cap <= arr->capacity) { /* Overflow */ \
return false; \
} \
const size_t buf_size = new_cap * sizeof(TYPE); \
if (buf_size < new_cap) { /* Overflow */ \
return false; \
} \
TYPE *data_new = realloc(arr->data, buf_size); \
if (!data_new) { \
return false; \
} \
arr->data = data_new; \
arr->capacity = new_cap; \
} \
assert(arr->size < arr->capacity); \
const size_t new_last_idx = arr->size; \
for (size_t i = new_last_idx; i > idx; --i) { \
arr->data[i] = arr->data[i - 1]; \
} \
if (arr->elem_cpy) { \
arr->data[idx] = arr->elem_cpy(elem); \
} else { \
arr->data[idx] = elem; \
} \
++arr->size; \
return true; \
}\
\
void guf_darr_##TYPENAME##_pop_back(guf_darr_##TYPENAME *arr) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return; \
}\
if (arr->size == 0) { \
return; \
} \
if (arr->elem_free) { \
arr->elem_free(arr->data[arr->size - 1]); \
} \
--arr->size; \
}\
\
TYPE *guf_darr_##TYPENAME##_back(const guf_darr_##TYPENAME *arr) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return NULL; \
}\
if (arr->size == 0) { \
return NULL; \
} \
return arr->data + (arr->size - 1);\
}\
\
TYPE *guf_darr_##TYPENAME##_front(const guf_darr_##TYPENAME *arr) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return NULL; \
}\
if (arr->size == 0) { \
return NULL; \
} \
return arr->data + 0;\
}\
\
TYPE *guf_darr_##TYPENAME##_at(const guf_darr_##TYPENAME *arr, size_t idx) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return NULL; \
}\
if (idx >= arr->size) { \
return NULL; \
} \
assert(arr->size != 0); \
return arr->data + idx; \
}\
\
bool guf_darr_##TYPENAME##_erase_at(guf_darr_##TYPENAME *arr, size_t idx) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return false; \
}\
if (idx >= arr->size) { \
return false; \
} \
assert(arr->size != 0); \
if (arr->elem_free) { \
arr->elem_free(arr->data[idx]); \
} \
if (idx == arr->size - 1) { \
--arr->size; \
return true; \
}\
if (idx + 1 < idx) { /* Overflow */ \
return false; \
} \
for (size_t i = idx + 1; i < arr->size; ++i) { \
arr->data[i - 1] = arr->data[i]; \
} \
--arr->size; \
return true; \
}\
\
bool guf_darr_##TYPENAME##_shrink_to_fit(guf_darr_##TYPENAME *arr) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return false; \
}\
if (arr->size == arr->capacity) { \
return true; \
}\
const size_t new_cap = arr->size == 0 ? 1 : arr->size; \
TYPE *data_new = realloc(arr->data, sizeof(TYPE) * new_cap); \
if (!data_new) { \
return false; \
} \
arr->data = data_new; \
arr->capacity = new_cap; \
return true; \
}\
\
bool guf_darr_##TYPENAME##_free(guf_darr_##TYPENAME *arr) { \
bool valid = arr && arr->data && arr->capacity && arr->size <= arr->capacity; \
assert(valid); \
if (!valid) { \
return false; \
}\
if (arr->elem_free) { \
for (size_t i = 0; i < arr->size; ++i) { \
arr->elem_free(arr->data[i]); \
} \
} \
free(arr->data); \
arr->data = NULL; \
arr->capacity = arr->size = 0; \
return true; \
}\
#endif

View File

@ -1,260 +0,0 @@
#include <string.h>
#include "guf_dbuf.h"
#include "guf_common.h"
static inline bool dbuf_valid_and_not_empty(const guf_dbuf* dbuf) {
return dbuf && guf_obj_meta_sizeof_obj(dbuf->elem_meta) > 0 && dbuf->data && dbuf->capacity > 0 && dbuf->size > 0 && dbuf->size <= dbuf->capacity;
}
static inline bool dbuf_valid_and_maybe_empty(const guf_dbuf* dbuf) {
GUF_ASSERT_RELEASE((!dbuf->data && !dbuf->capacity) || (dbuf->data && dbuf->capacity));
return dbuf && guf_obj_meta_sizeof_obj(dbuf->elem_meta) > 0 && dbuf->capacity >= 0 && dbuf->size >= 0 && dbuf->size <= dbuf->capacity;
}
bool guf_dbuf_reserve(guf_dbuf *dbuf, ptrdiff_t min_capacity)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_maybe_empty(dbuf));
GUF_ASSERT_RELEASE(min_capacity >= 0);
const ptrdiff_t sizeof_elem = guf_obj_meta_sizeof_obj(dbuf->elem_meta);
if (min_capacity <= dbuf->capacity) {
return true;
}
if (!dbuf->data) {
GUF_ASSERT_RELEASE(guf_is_safe_size_calc(min_capacity, sizeof_elem));
void *data = calloc(min_capacity, sizeof_elem);
GUF_ASSERT(data);
if (!data) {
return false;
}
dbuf->data = data;
} else {
void *data = realloc(dbuf->data, guf_safe_size_calc(min_capacity, sizeof_elem));
GUF_ASSERT(data);
if (!data) {
return false;
}
dbuf->data = data;
}
dbuf->capacity = min_capacity;
return true;
}
bool guf_dbuf_init(guf_dbuf *dbuf, guf_obj_meta elem_meta, ptrdiff_t start_cap)
{
GUF_ASSERT_RELEASE(dbuf);
GUF_ASSERT_RELEASE(start_cap >= 0);
const ptrdiff_t sizeof_elem = guf_obj_meta_sizeof_obj(elem_meta);
GUF_ASSERT_RELEASE(sizeof_elem > 0);
dbuf->elem_meta = elem_meta;
dbuf->size = dbuf->capacity = 0;
if (start_cap == 0) {
dbuf->data = NULL;
return true;
}
bool success = guf_dbuf_reserve(dbuf, start_cap);
if (success) {
dbuf->capacity = start_cap;
}
GUF_ASSERT(success);
return success;
}
guf_dbuf guf_dbuf_new(guf_obj_meta elem_meta)
{
guf_dbuf dbuf = {0};
bool success = guf_dbuf_init(&dbuf, elem_meta, 0);
GUF_ASSERT_RELEASE(success);
return dbuf;
}
guf_dbuf guf_dbuf_new_with_capacity(guf_obj_meta elem_meta, ptrdiff_t capacity)
{
GUF_ASSERT_RELEASE(capacity >= 0);
guf_dbuf dbuf = {0};
bool success = guf_dbuf_init(&dbuf, elem_meta, capacity);
GUF_ASSERT_RELEASE(success);
return dbuf;
}
static inline void *get_elem(guf_dbuf *dbuf, ptrdiff_t idx)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
GUF_ASSERT_RELEASE(idx >= 0);
GUF_ASSERT_RELEASE(idx < dbuf->size && idx < dbuf->capacity);
char *ptr = (char*)dbuf->data;
return ptr + guf_safe_size_calc(idx, guf_obj_meta_sizeof_obj(dbuf->elem_meta));
}
static inline ptrdiff_t next_capacity(ptrdiff_t old_cap)
{
GUF_ASSERT_RELEASE(old_cap >= 0);
size_t new_cap = 0;
if (old_cap == 0) {
new_cap = GUF_DBUF_INITIAL_CAP;
} else if (old_cap < 8) {
new_cap = (size_t)old_cap * 2ull;
} else {
new_cap = (size_t)old_cap * 3ull / 2ull;
}
GUF_ASSERT_RELEASE(new_cap > (size_t)old_cap); // Fail on overflow.
GUF_ASSERT_RELEASE(new_cap <= PTRDIFF_MAX);
return new_cap;
}
static inline bool grow_if_full(guf_dbuf *dbuf)
{
GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0);
if (dbuf->size == dbuf->capacity) {
bool success = guf_dbuf_reserve(dbuf, next_capacity(dbuf->capacity));
if (!success) {
return false;
}
}
GUF_ASSERT_RELEASE(dbuf->size < dbuf->capacity);
return true;
}
static inline void *cpy_to(guf_dbuf *dbuf, ptrdiff_t idx, void *elem, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
GUF_ASSERT_RELEASE(elem);
GUF_ASSERT_RELEASE(idx >= 0 && idx < dbuf->capacity && idx < dbuf->size);
void *dst = get_elem(dbuf, idx);
dst = guf_cpy(dst, elem, dbuf->elem_meta, cpy_opt);
GUF_ASSERT_RELEASE(dst);
return dst;
}
void *guf_dbuf_push(guf_dbuf *dbuf, void *elem, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_maybe_empty(dbuf));
bool success = grow_if_full(dbuf);
GUF_ASSERT(success);
if (!success) {
return NULL;
}
return cpy_to(dbuf, dbuf->size++, elem, cpy_opt);
}
void *guf_dbuf_insert(guf_dbuf *dbuf, void *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_maybe_empty(dbuf));
GUF_ASSERT_RELEASE(idx >= 0 && idx <= dbuf->size);
if (idx == dbuf->size) {
return guf_dbuf_push(dbuf, elem, cpy_opt);
}
GUF_ASSERT_RELEASE(idx < dbuf->size);
bool success = grow_if_full(dbuf);
GUF_ASSERT(success);
if (!success) {
return NULL;
}
for (ptrdiff_t free_idx = dbuf->size++; free_idx > idx; --free_idx) {
void *dst = get_elem(dbuf, free_idx);
void *src = get_elem(dbuf, free_idx - 1);
guf_cpy(dst, src, dbuf->elem_meta, GUF_CPY_VALUE);
}
return cpy_to(dbuf, idx, elem, cpy_opt);
}
void guf_dbuf_erase(guf_dbuf *dbuf, ptrdiff_t idx)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
GUF_ASSERT_RELEASE(idx >= 0);
GUF_ASSERT_RELEASE(idx < dbuf->size);
void *to_erase = get_elem(dbuf, idx);
if (dbuf->elem_meta.has_ops && dbuf->elem_meta.data.ops->free) {
dbuf->elem_meta.data.ops->free(to_erase);
}
for (ptrdiff_t free_idx = idx; free_idx < dbuf->size - 1; ++free_idx) {
void *dst = get_elem(dbuf, free_idx);
void *src = get_elem(dbuf, free_idx + 1);
guf_cpy(dst, src, dbuf->elem_meta, GUF_CPY_VALUE);
}
}
void *guf_dbuf_pop(guf_dbuf *dbuf)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
void *popped = get_elem(dbuf, dbuf->size - 1);
dbuf->size -= 1;
return popped;
}
void *guf_dbuf_at(guf_dbuf *dbuf, ptrdiff_t idx)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
GUF_ASSERT_RELEASE(idx >= 0 && idx < dbuf->size)
return get_elem(dbuf, idx);
}
void *guf_dbuf_front(guf_dbuf *dbuf)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
return get_elem(dbuf, 0);
}
void *guf_dbuf_back(guf_dbuf *dbuf)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_not_empty(dbuf));
return get_elem(dbuf, (ptrdiff_t)dbuf->size - 1);
}
bool guf_dbuf_shrink_to_fit(guf_dbuf *dbuf)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_maybe_empty(dbuf));
const ptrdiff_t new_capacity = dbuf->size;
if (new_capacity == dbuf->capacity) {
return true;
}
GUF_ASSERT_RELEASE(dbuf->data);
void *data = realloc(dbuf->data, guf_safe_size_calc(new_capacity, guf_obj_meta_sizeof_obj(dbuf->elem_meta)));
GUF_ASSERT(data);
if (!data) {
return false;
}
dbuf->data = data;
dbuf->capacity = new_capacity;
return true;
}
void guf_dbuf_free(guf_dbuf *dbuf)
{
GUF_ASSERT_RELEASE(dbuf_valid_and_maybe_empty(dbuf));
if (dbuf->capacity == 0) {
GUF_ASSERT_RELEASE(!dbuf->data);
GUF_ASSERT_RELEASE(dbuf->size == 0);
return;
}
GUF_ASSERT_RELEASE(dbuf->data);
if (dbuf->elem_meta.has_ops && dbuf->elem_meta.data.ops->free) {
for (ptrdiff_t idx = 0; idx < dbuf->size; ++idx) {
// printf("freeing %s\n",*(char**)get_elem(dbuf, idx));
dbuf->elem_meta.data.ops->free(get_elem(dbuf, idx));
}
}
free(dbuf->data);
dbuf->data = NULL;
dbuf->capacity = dbuf->size = 0;
}

View File

@ -1,81 +1,355 @@
#ifndef GUF_DBUF_H
#define GUF_DBUF_H
#include <stddef.h> #include <stddef.h>
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "guf_assert.h" #include "guf_assert.h"
#include "guf_obj.h" #include "guf_common.h"
#ifndef GUF_DBUF_H
#define GUF_DBUF_H
#define GUF_DBUF_FOREACH(DBUF, ELEM_TYPE, ELEM_PTR_NAME) for (ELEM_TYPE *ELEM_PTR_NAME = (DBUF).data, *end = (DBUF).data ? (DBUF).data + (DBUF).size : NULL; ELEM_PTR_NAME != end; ++ELEM_PTR_NAME)
#endif
// Used for the first growth if dbuf->capacity is zero. // Used for the first growth if dbuf->capacity is zero.
#define GUF_DBUF_INITIAL_CAP 8 #ifndef GUF_DBUF_INITIAL_CAP
#define GUF_DBUF_INITIAL_CAP 8
typedef struct guf_dbuf {
bool is_init;
void *data;
ptrdiff_t size, capacity;
guf_obj_meta elem_meta;
} guf_dbuf;
bool guf_dbuf_init(guf_dbuf *dbuf, guf_obj_meta elem_meta, ptrdiff_t start_cap);
guf_dbuf guf_dbuf_new_with_capacity(guf_obj_meta elem_meta, ptrdiff_t start_cap);
guf_dbuf guf_dbuf_new(guf_obj_meta elem_meta);
void guf_dbuf_free(guf_dbuf *dbuf);
bool guf_dbuf_reserve(guf_dbuf *dbuf, ptrdiff_t min_capacity);
bool guf_dbuf_shrink_to_fit(guf_dbuf *dbuf);
void *guf_dbuf_push(guf_dbuf *dbuf, void *elem, guf_obj_cpy_opt cpy_opt);
void *guf_dbuf_insert(guf_dbuf *dbuf, void *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt);
void guf_dbuf_erase(guf_dbuf *dbuf, ptrdiff_t idx);
void *guf_dbuf_pop(guf_dbuf *dbuf);
void *guf_dbuf_at(guf_dbuf *dbuf, ptrdiff_t idx);
void *guf_dbuf_front(guf_dbuf *dbuf);
void *guf_dbuf_back(guf_dbuf *dbuf);
void guf_dbuf_sort(guf_dbuf *dbuf, void (*cmp)(const void *a, const void *b));
void guf_dbuf_ascending(guf_dbuf *dbuf);
void guf_dbuf_descending(guf_dbuf *dbuf);
// Convenience macros:
#define GUF_DBUF_NEW(elem_type) guf_dbuf_new((guf_obj_meta){.has_ops = false, .data.sizeof_obj = sizeof(elem_type)})
#define GUF_DBUF_NEW_WITH_CAP(elem_type, capacity) guf_dbuf_new_with_capacity((guf_obj_meta){.has_ops = false, .data.sizeof_obj = sizeof(elem_type)}, capacity)
// #define GUF_DBUF_NEW_FROM_OPS(obj_ops_ptr) guf_dbuf_new((guf_obj_meta){.has_ops = true, .data = obj_ops_ptr})
// #define GUF_DBUF_NEW_FROM_OPS_WITH_CAP(obj_ops_ptr, capacity) guf_dbuf_new_with_capacity(-1, (obj_ops_ptr), (capacity))
#define GUF_DBUF_PUSH_VAL(dbuf_ptr, elem_type, elem_val) do { \
elem_type tmp_lvalue = elem_val; \
void *res = guf_dbuf_push(dbuf_ptr, &tmp_lvalue, GUF_CPY_VALUE); \
GUF_ASSERT_RELEASE(res); \
} while (0); \
#define GUF_DBUF_PUSH_VAL_CPY(dbuf_ptr, elem_type, elem_val) do { \
elem_type tmp_lvalue = elem_val; \
void *res = guf_dbuf_push(dbuf_ptr, &tmp_lvalue, GUF_CPY_DEEP); \
GUF_ASSERT_RELEASE(res); \
} while (0); \
#define GUF_DBUF_TRY_PUSH_VAL(dbuf_ptr, elem_type, elem_val, success_bool_name) do { \
elem_type tmp_lvalue = elem_val; \
void *res = guf_dbuf_push(dbuf_ptr, &tmp_lvalue, GUF_CPY_VALUE); \
success_bool_name = res != NULL; \
} while (0); \
#define GUF_DBUF_TRY_PUSH_VAL_CPY(dbuf_ptr, elem_type, elem_val, success_bool_name); do { \
elem_type tmp_lvalue = elem_val; \
void *res = guf_dbuf_push(dbuf_ptr, &tmp_lvalue, GUF_CPY_DEEP); \
success_bool_name = res != NULL; \
} while (0); \
#define GUF_DBUF_AT_VAL(dbuf_ptr, elem_type, idx) *(elem_type*)guf_dbuf_at(dbuf_ptr, idx)
#define GUF_DBUF_POP_VAL(dbuf_ptr, elem_type) *(elem_type*)guf_dbuf_pop(dbuf_ptr)
#define GUF_DBUF_LAST_VAL(dbuf_ptr, elem_type) *(elem_type*)guf_dbuf_back(dbuf_ptr)
#define GUF_DBUF_FIRST_VAL(dbuf_ptr, elem_type) *(elem_type*)guf_dbuf_front(dbuf_ptr)
#define GUF_DBUF_FOREACH(DBUF, ELEM_TYPE, ELEM_PTR_NAME) for (ELEM_TYPE *ELEM_PTR_NAME = (ELEM_TYPE*)(DBUF).data, *end = ((ELEM_TYPE*)(DBUF).data) + (DBUF).size; ELEM_PTR_NAME != end; ++ELEM_PTR_NAME)
#endif #endif
#ifndef GUF_CNT_T
#error "Undefined container template GUF_CONTAINER_T"
#endif
#ifndef GUF_CNT_NAME
#error "Undefined container template GUF_CONTAINER_T"
#endif
#ifndef GUF_CNT_T_OPS
#define GUF_OBJ_TYPE GUF_CNT_T
// #define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_CNT_T, _ops)
#include "guf_obj.h"
#define GUF_CNT_T_OPS GUF_OBJ_OPS_EMPTY(GUFCAT(GUF_CNT_T, _ops))
#endif
#define GUF_CNT_T_OPS_CPY GUFCAT(GUFCAT(GUF_CNT_T, _ops), _cpy)
typedef struct GUF_CNT_NAME {
GUF_CNT_T *data;
ptrdiff_t size, capacity;
} GUF_CNT_NAME;
static inline bool GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(const GUF_CNT_NAME* dbuf)
{
return dbuf && dbuf->data && dbuf->capacity > 0 && dbuf->size > 0 && dbuf->size <= dbuf->capacity;
}
static inline bool GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(const GUF_CNT_NAME* dbuf)
{
GUF_ASSERT_RELEASE((!dbuf->data && !dbuf->capacity) || (dbuf->data && dbuf->capacity));
return dbuf && dbuf->capacity >= 0 && dbuf->size >= 0 && dbuf->size <= dbuf->capacity;
}
bool GUFCAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf));
GUF_ASSERT_RELEASE(min_capacity >= 0);
if (min_capacity <= dbuf->capacity) {
return true;
}
if (!dbuf->data) {
GUF_CNT_T *data = calloc(min_capacity, sizeof(GUF_CNT_T));
GUF_ASSERT(data);
if (!data) {
return false;
}
dbuf->data = data;
} else {
GUF_CNT_T *data = realloc(dbuf->data, guf_safe_size_calc(min_capacity, sizeof(GUF_CNT_T)));
GUF_ASSERT(data);
if (!data) {
return false;
}
dbuf->data = data;
}
dbuf->capacity = min_capacity;
return true;
}
static inline bool GUFCAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap)
{
GUF_ASSERT_RELEASE(dbuf);
GUF_ASSERT_RELEASE(start_cap >= 0);
dbuf->size = dbuf->capacity = 0;
if (start_cap == 0) {
dbuf->data = NULL;
return true;
}
bool success = GUFCAT(GUF_CNT_NAME, _reserve)(dbuf, start_cap);
if (success) {
dbuf->capacity = start_cap;
}
GUF_ASSERT(success);
return success;
}
static inline GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t start_cap)
{
GUF_ASSERT_RELEASE(start_cap >= 0);
GUF_CNT_NAME dbuf = {0};
bool success = GUFCAT(GUF_CNT_NAME, _init)(&dbuf, start_cap);
GUF_ASSERT_RELEASE(success);
return dbuf;
}
GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new)(void)
{
GUF_CNT_NAME dbuf = {0};
bool success = GUFCAT(GUF_CNT_NAME, _init)(&dbuf, 0);
GUF_ASSERT_RELEASE(success);
return dbuf;
}
static inline ptrdiff_t GUFCAT(GUF_CNT_NAME, _next_capacity)(ptrdiff_t old_cap)
{
GUF_ASSERT_RELEASE(old_cap >= 0);
size_t new_cap = 0;
if (old_cap == 0) {
new_cap = GUF_DBUF_INITIAL_CAP;
} else if (old_cap < 8) {
new_cap = (size_t)old_cap * 2ull;
} else {
new_cap = (size_t)old_cap * 3ull / 2ull;
}
GUF_ASSERT_RELEASE(new_cap > (size_t)old_cap); // Fail on overflow.
GUF_ASSERT_RELEASE(new_cap <= PTRDIFF_MAX);
return new_cap;
}
static inline bool GUFCAT(GUF_CNT_NAME, _grow_if_full)(GUF_CNT_NAME *dbuf)
{
GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0);
if (dbuf->size == dbuf->capacity) {
bool success = GUFCAT(GUF_CNT_NAME, _reserve)(dbuf, GUFCAT(GUF_CNT_NAME, _next_capacity)(dbuf->capacity));
if (!success) {
return false;
}
}
GUF_ASSERT_RELEASE(dbuf->size < dbuf->capacity);
return true;
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME,_valid_and_maybe_empty)(dbuf));
GUF_ASSERT_RELEASE(!(cpy_opt == GUF_CPY_VALUE && GUF_CNT_T_OPS.free)); // Don't allow copy by value if there's a free operation.
bool success = GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf);
GUF_ASSERT(success);
if (!success) {
return NULL;
}
GUF_CNT_T *dst = dbuf->data + dbuf->size++;
return GUF_CNT_T_OPS_CPY(dst, elem, &GUF_CNT_T_OPS, cpy_opt);
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem)
{
GUF_ASSERT_RELEASE(!GUF_CNT_T_OPS.free); // Don't allow copy by value if there's a free operation.
return GUFCAT(GUF_CNT_NAME, _push)(dbuf, &elem, GUF_CPY_VALUE);
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem)
{
return GUFCAT(GUF_CNT_NAME, _push)(dbuf, &elem, GUF_CPY_DEEP);
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME,_valid_and_maybe_empty)(dbuf));
GUF_ASSERT_RELEASE(idx >= 0 && idx <= dbuf->size);
GUF_ASSERT_RELEASE(!(cpy_opt == GUF_CPY_VALUE && GUF_CNT_T_OPS.free)); // Don't allow copy by value if there's a free operation.
if (idx == dbuf->size) {
return GUFCAT(GUF_CNT_NAME, _push)(dbuf, elem, cpy_opt);
}
GUF_ASSERT_RELEASE(idx < dbuf->size);
bool success = GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf);
GUF_ASSERT(success);
if (!success) {
return NULL;
}
for (ptrdiff_t free_idx = dbuf->size++; free_idx > idx; --free_idx) {
GUF_CNT_T *dst = dbuf->data + free_idx;
GUF_CNT_T *src = dst - 1;
GUF_CNT_T_OPS_CPY(dst, src, &GUF_CNT_T_OPS, GUF_CPY_VALUE);
}
return GUF_CNT_T_OPS_CPY(dbuf->data + idx, elem, &GUF_CNT_T_OPS, cpy_opt);
}
void GUFCAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf));
GUF_ASSERT_RELEASE(idx >= 0);
GUF_ASSERT_RELEASE(idx < dbuf->size);
if (GUF_CNT_T_OPS.free) {
GUF_CNT_T_OPS.free(dbuf->data + idx);
}
for (ptrdiff_t free_idx = idx; free_idx < dbuf->size - 1; ++free_idx) {
GUF_CNT_T *dst = dbuf->data + free_idx;
GUF_CNT_T *src = dst + 1;
GUF_CNT_T_OPS_CPY(dst, src, &GUF_CNT_T_OPS, GUF_CPY_VALUE);
}
}
void GUFCAT(GUF_CNT_NAME, pop)(GUF_CNT_NAME *dbuf)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf));
GUF_CNT_T *popped = dbuf->data + --dbuf->size;
if (GUF_CNT_T_OPS.free) {
GUF_CNT_T_OPS.free(popped);
}
}
GUF_CNT_T GUFCAT(GUF_CNT_NAME, pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf));
GUF_CNT_T *popped = dbuf->data + --dbuf->size;
GUF_CNT_T popped_val;
GUF_CNT_T *success = GUF_CNT_T_OPS_CPY(&popped_val, popped, &GUF_CNT_T_OPS, cpy_opt);
GUF_ASSERT_RELEASE(success);
if (cpy_opt == GUF_CPY_DEEP && GUF_CNT_T_OPS.free) {
GUF_CNT_T_OPS.free(popped);
}
return popped_val;
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf));
GUF_ASSERT_RELEASE(idx >= 0 && idx < dbuf->size)
return dbuf->data + idx;
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf));
return dbuf->data + 0;
}
GUF_CNT_T *GUFCAT(GUF_CNT_NAME, back)(GUF_CNT_NAME *dbuf)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf));
return dbuf->data + (dbuf->size - 1);
}
bool GUFCAT(GUF_CNT_NAME, _shrink_to_fit)(GUF_CNT_NAME *dbuf)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf));
const ptrdiff_t new_capacity = dbuf->size;
if (new_capacity == dbuf->capacity) {
return true;
}
GUF_ASSERT_RELEASE(new_capacity < dbuf->capacity);
GUF_ASSERT_RELEASE(dbuf->data);
GUF_CNT_T *data = realloc(dbuf->data, guf_safe_size_calc(new_capacity, sizeof(GUF_CNT_T)));
GUF_ASSERT(data);
if (!data) {
return false;
}
dbuf->data = data;
dbuf->capacity = new_capacity;
return true;
}
void GUFCAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf)
{
GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf));
if (dbuf->capacity == 0) {
GUF_ASSERT_RELEASE(!dbuf->data);
GUF_ASSERT_RELEASE(dbuf->size == 0);
return;
}
GUF_ASSERT_RELEASE(dbuf->data);
if (GUF_CNT_T_OPS.free) {
for (ptrdiff_t idx = 0; idx < dbuf->size; ++idx) {
GUF_CNT_T_OPS.free(dbuf->data + idx);
}
}
free(dbuf->data);
dbuf->data = NULL;
dbuf->capacity = dbuf->size = 0;
}
// void guf_dbuf_sort(guf_dbuf *dbuf, void (*cmp)(const void *a, const void *b));
// void guf_dbuf_ascending(guf_dbuf *dbuf);
// void guf_dbuf_descending(guf_dbuf *dbuf);
typedef struct GUFCAT(GUF_CNT_NAME, _iter) {
GUF_CNT_T *cur, *end, *begin;
struct GUFCAT(GUF_CNT_NAME, _iter) (*next)(const struct GUFCAT(GUF_CNT_NAME, _iter) *it, ptrdiff_t step);
} GUFCAT(GUF_CNT_NAME, _iter);
static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _iter_next)(const GUFCAT(GUF_CNT_NAME, _iter) *it, ptrdiff_t step)
{
GUF_ASSERT_RELEASE(it);
if (it->begin == NULL && it->end == NULL) {
return *it;
}
GUF_ASSERT_RELEASE(it->begin != NULL && it->end != NULL);
GUF_ASSERT_RELEASE(it->cur < it->end);
GUFCAT(GUF_CNT_NAME, _iter) next_it = *it;
intptr_t next_ptr = (intptr_t)it->cur + step * sizeof(GUF_CNT_T);
if (next_ptr >= (intptr_t)it->end) {
next_it.cur = it->end;
} else if (next_ptr < (intptr_t)it->begin) { // TODO: different end for begin
next_it.cur = it->end;
} else {
GUF_ASSERT_RELEASE(next_ptr >= (intptr_t)it->begin);
GUF_ASSERT_RELEASE(next_ptr <= (intptr_t)it->end);
GUF_ASSERT_RELEASE(it->cur + step >= it->begin && it->cur + step <= it->end);
next_it.cur = it->cur + step;
}
return next_it;
}
static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf)
{
GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf);
return (GUFCAT(GUF_CNT_NAME, _iter)) {
.cur = dbuf->data,
.begin = dbuf->data,
.end = dbuf->data ? dbuf->data + dbuf->size : NULL,
.next = GUFCAT(GUF_CNT_NAME, _iter_next)
};
}
static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf)
{
GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf);
return (GUFCAT(GUF_CNT_NAME, _iter)) {
.cur = dbuf->data ? dbuf->data + dbuf->size : NULL,
.begin = dbuf->data,
.end = dbuf->data ? dbuf->data + dbuf->size : NULL,
.next = GUFCAT(GUF_CNT_NAME, _iter_next)
};
}
#undef GUF_DBUF_INITIAL_CAP
#undef GUF_CNT_T
#undef GUF_CNT_T_OPS
#undef GUF_CNT_T_OPS_CPY
#undef GUF_CNT_NAME

View File

@ -12,24 +12,24 @@ typedef enum guf_dict_probe_type {GUF_DICT_PROBE_LINEAR = 0, GUF_DICT_PROBE_QUAD
#define GUF_DICT_MAX_LOAD_FAC_FX10_DEFAULT 666ul #define GUF_DICT_MAX_LOAD_FAC_FX10_DEFAULT 666ul
#define GUF_DICT_PROBE_TYPE_DEFAULT GUF_DICT_PROBE_QUADRATIC #define GUF_DICT_PROBE_TYPE_DEFAULT GUF_DICT_PROBE_QUADRATIC
#define GUF_HASH32_INIT 2166136261ul // #define GUF_HASH32_INIT 2166136261ul
#define GUF_HASH64_INIT 14695981039346656037ull // #define GUF_HASH64_INIT 14695981039346656037ull
uint32_t guf_hash32(const void *data, size_t num_bytes, uint32_t hash); // uint32_t guf_hash32(const void *data, size_t num_bytes, uint32_t hash);
uint64_t guf_hash64(const void *data, size_t num_bytes, uint64_t hash); // uint64_t guf_hash64(const void *data, size_t num_bytes, uint64_t hash);
#ifdef GUF_DICT_USE_32_BIT_HASH // #ifdef GUF_USE_32_BIT_HASH
#define GUF_HASH_INIT GUF_HASH32_INIT // #define GUF_HASH_INIT GUF_HASH32_INIT
static inline uint32_t guf_hash(const void *data, size_t num_bytes, uint32_t hash) { // static inline uint32_t guf_hash(const void *data, size_t num_bytes, uint32_t hash) {
return guf_hash32(data, num_bytes, hash); // return guf_hash32(data, num_bytes, hash);
} // }
#define GUF_DICT_HASH_MAX UINT32_MAX // #define GUF_DICT_HASH_MAX UINT32_MAX
#else // #else
#define GUF_HASH_INIT GUF_HASH64_INIT // #define GUF_HASH_INIT GUF_HASH64_INIT
static inline uint64_t guf_hash(const void *data, size_t num_bytes, uint64_t hash) { // static inline uint64_t guf_hash(const void *data, size_t num_bytes, uint64_t hash) {
return guf_hash64(data, num_bytes, hash); // return guf_hash64(data, num_bytes, hash);
} // }
#define GUF_DICT_HASH_MAX UINT64_MAX // #define GUF_DICT_HASH_MAX UINT64_MAX
#endif // #endif
typedef struct guf_dict_kv_funcs { typedef struct guf_dict_kv_funcs {
// Only used for keys: // Only used for keys:

59
src/guf_hash.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef GUF_HASH_H
#define GUF_HASH_H
#include "guf_common.h"
// #define GUF_USE_32_BIT_HASH /* Define GUF_USE_32_BIT_HASH to make guflib use 32 bit hashes */
/*
FNV-1a (32-bit and 64-bit) hash functions.
Generally, you should always call csr_hash with GUF_HASH_INIT as the hash argument, unless you want to create "chains" of hashes.
cf. http://www.isthe.com/chongo/tech/comp/fnv/ (last retrieved: 2023-11-30)
*/
#define GUF_HASH32_INIT 2166136261ul
#define GUF_HASH64_INIT 14695981039346656037ull
static inline uint32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint32_t hash)
{
GUF_ASSERT_RELEASE(data);
GUF_ASSERT_RELEASE(num_bytes > 0);
const unsigned char *data_bytes = (const unsigned char*)data; // This does not break strict-aliasing rules I think...
const uint32_t FNV_32_PRIME = 16777619ul;
for (size_t i = 0; i < (size_t)num_bytes; ++i) {
hash ^= data_bytes[i];
hash *= FNV_32_PRIME;
}
return hash;
}
static inline uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint64_t hash)
{
GUF_ASSERT_RELEASE(data);
GUF_ASSERT_RELEASE(num_bytes > 0);
const unsigned char *data_bytes = (const unsigned char*)data; // This does not break strict-aliasing rules I think...
const uint64_t FNV_64_PRIME = 1099511628211ull;
for (size_t i = 0; i < (size_t)num_bytes; ++i) {
hash ^= data_bytes[i];
hash *= FNV_64_PRIME;
}
return hash;
}
#ifdef GUF_USE_32_BIT_HASH
typedef uint32_t guf_hash_size_t;
#define GUF_HASH_INIT GUF_HASH32_INIT
static inline guf_hash_size_t guf_hash(const void *data, ptrdiff_t num_bytes, uint32_t hash) {
return guf_hash32(data, num_bytes, hash);
}
#define GUF_HASH_MAX UINT32_MAX
#else
typedef uint64_t guf_hash_size_t;
#define GUF_HASH_INIT GUF_HASH64_INIT
static inline guf_hash_size_t guf_hash(const void *data, ptrdiff_t num_bytes, uint64_t hash) {
return guf_hash64(data, num_bytes, hash);
}
#define GUF_HASH_MAX UINT64_MAX
#endif
#endif

View File

@ -1,74 +0,0 @@
#include "guf_obj.h"
static int cstr_ptr_cmp(const void *a, const void *b){
GUF_ASSERT_RELEASE(a && b);
// typeof dst/src: pointer to const char* (const char**)
const char **a_ptr = (const char**)a;
const char **b_ptr = (const char**)b;
return strcmp(*a_ptr, *b_ptr);
}
static GUF_OBJ_DEFINE_CMP_DESC(cstr_ptr_cmp, cstr_ptr_cmp_desc)
static bool cstr_ptr_eq(const void *a, const void *b) {
GUF_ASSERT_RELEASE(a && b);
return 0 == cstr_ptr_cmp(a, b);
}
static void *cstr_default_init(void *dst)
{
GUF_ASSERT_RELEASE(dst);
char **dst_ptr = (char**)dst;
*dst_ptr = NULL;
return dst;
}
static void *cstr_cpy_init(void *dst, const void *src)
{
GUF_ASSERT_RELEASE(dst && src);
char **dst_ptr = (char**)dst;
const char **src_ptr = (const char**)src;
char *cpy = strdup(*src_ptr);
GUF_ASSERT_RELEASE(cpy);
*dst_ptr = cpy;
return dst;
}
void *cstr_move_init(void *dst, void *src)
{
GUF_ASSERT_RELEASE(dst && src);
char **dst_ptr = (char**)dst;
char **src_ptr = (char**)src;
*dst_ptr = *src_ptr;
*src_ptr = NULL;
return dst;
}
void cstr_ptr_free(void *ptr)
{
GUF_ASSERT_RELEASE(ptr);
char **cstr_ptr = (char**)ptr;
free(*cstr_ptr);
}
static const guf_obj_ops guf_cstr_ops = {
.sizeof_obj = sizeof(guf_cstr_type),
.cmp = cstr_ptr_cmp,
.cmp_desc = cstr_ptr_cmp_desc,
.eq = cstr_ptr_eq ,
.default_init = cstr_default_init,
.copy_init = cstr_cpy_init,
.move_init = cstr_move_init,
.free = cstr_ptr_free,
.hash = NULL,
};
const guf_obj_meta guf_cstr_obj_meta = {
.has_ops = true,
.data.ops = &guf_cstr_ops
};
const guf_obj_meta guf_const_cstr_obj_meta = {
.has_ops = false,
.data.sizeof_obj = sizeof(guf_const_cstr_type)
};

View File

@ -1,127 +1,48 @@
#ifndef GUF_OBJ_H
#define GUF_OBJ_H
#include <string.h> #include <string.h>
#include "guf_common.h" #include "guf_common.h"
typedef enum guf_obj_cpy_opt { #ifndef GUF_OBJ_H
GUF_CPY_VALUE = 0, #define GUF_OBJ_H
GUF_CPY_DEEP = 1, #define GUF_OBJ_OPS_DEFINE_CMP_INVERSE(obj_type, cmp_fn, cmp_inverted_name) int cmp_inverted_name(const obj_type *a, const obj_type *b) {return -cmp_fn(a, b);}
GUF_CPY_MOVE = 2, #define GUF_OBJ_OPS_VALUE_TYPE(obj_ops_type, eq, cmp, cmp_inv) (obj_ops_type){.copy_init = NULL, .move_init = NULL, .free = NULL, .eq = eq, .cmp = cmp, .cmp_inv = cmp_inv}
} guf_obj_cpy_opt; #define GUF_OBJ_OPS_EMPTY(obj_ops_type) (obj_ops_type){.copy_init = NULL, .move_init = NULL, .free = NULL, .eq = NULL, .cmp = NULL, .cmp_inv = NULL}
typedef struct guf_obj_ops {
ptrdiff_t sizeof_obj;
void *(*default_init)(void *dst_obj); // Default constructor.
void *(*copy_init)(void *dst_obj, const void *src_obj); // Copy constructor (deep copies src).
void *(*move_init)(void *dst_obj, void *src_obj); // Move constructor ("steals" from src).
void (*free)(void *obj);
bool (*eq)(const void *obj_a, const void *obj_b);
int (*cmp)(const void *obj_a, const void *obj_b);
int (*cmp_desc)(const void *obj_a, const void *obj_b); // Define with GUF_OBJ_DEFINE_CMP_DESC
guf_hash_size_t (*hash)(const void *obj);
const char *type_str;
} guf_obj_ops;
#define GUF_OBJ_DEFINE_CMP_DESC(CMP_FN, CMP_DESC_FN_NAME) int CMP_DESC_FN_NAME(const void *a, const void *b) {return -CMP_FN(a, b);}
typedef struct guf_obj_meta {
bool has_ops;
union {
const guf_obj_ops *ops;
ptrdiff_t sizeof_obj;
} data;
} guf_obj_meta;
static inline ptrdiff_t guf_obj_meta_sizeof_obj(guf_obj_meta meta)
{
ptrdiff_t size = meta.has_ops ? meta.data.ops->sizeof_obj : meta.data.sizeof_obj;
GUF_ASSERT_RELEASE(size > 0);
return size;
}
static inline bool guf_obj_meta_same(guf_obj_meta a, guf_obj_meta b) {
if (a.has_ops != b.has_ops) {
return false;
}
if (a.has_ops) {
return a.data.ops == b.data.ops;
} else {
return a.data.sizeof_obj == b.data.sizeof_obj;
}
}
static inline void *guf_cpy(void *dst_ptr, void *src_ptr, guf_obj_meta obj_meta, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(dst_ptr);
GUF_ASSERT_RELEASE(src_ptr);
if (obj_meta.has_ops) {
GUF_ASSERT_RELEASE(obj_meta.data.ops != NULL);
}
const ptrdiff_t sizeof_obj = obj_meta.has_ops ? obj_meta.data.ops->sizeof_obj : obj_meta.data.sizeof_obj;
const guf_obj_ops *ops = obj_meta.has_ops ? obj_meta.data.ops : NULL;
GUF_ASSERT_RELEASE(sizeof_obj > 0);
switch (cpy_opt) {
case GUF_CPY_VALUE:
dst_ptr = memcpy(dst_ptr, src_ptr, sizeof_obj);
GUF_ASSERT_RELEASE(dst_ptr); // This should never fail.
break;
case GUF_CPY_DEEP:
GUF_ASSERT_RELEASE(ops->copy_init != NULL);
dst_ptr = ops->copy_init(dst_ptr, src_ptr);
break;
case GUF_CPY_MOVE:
GUF_ASSERT_RELEASE(ops->move_init != NULL);
dst_ptr = ops->move_init(dst_ptr, src_ptr);
break;
default:
GUF_ASSERT_RELEASE(false);
}
GUF_ASSERT(dst_ptr);
return dst_ptr;
}
static inline guf_obj_meta guf_obj_get_meta(void *obj)
{
guf_obj_meta *meta_ptr = (guf_obj_meta*)obj; // IMPORTANT: Assumes the obj's first member is of type guf_obj_meta, otherwise this is undefined behaviour.
GUF_ASSERT_RELEASE(meta_ptr);
return *meta_ptr;
}
static inline void *guf_obj_copy(void *dst_obj, void *src_obj, guf_obj_cpy_opt cpy_opts)
{
guf_obj_meta meta = guf_obj_get_meta(dst_obj);
guf_obj_meta meta_src = guf_obj_get_meta(src_obj);
GUF_ASSERT_RELEASE(guf_obj_meta_same(meta, meta_src));
return guf_cpy(dst_obj, src_obj, meta, cpy_opts);
}
#define GUF_OBJ_META_NAME guf_obj_meta_member
#define GUF_OBJ_DECLARE_OBJ_META() guf_obj_meta GUF_OBJ_META_NAME;
#define GUF_OBJ_GET_META_TYPESAFE(PTR) (PTR)->GUF_OBJ_META_NAME
#define GUF_OBJ_FREE_TYPESAFE(ptr) if (GUF_OBJ_GET_META_TYPESAFE(ptr).has_ops && GUF_OBJ_GET_META_TYPESAFE(ptr).data.ops->free) {GUF_OBJ_GET_META_TYPESAFE(ptr).data.ops->free(ptr);}
#define GUF_OBJ_FREE(PTR) if (guf_obj_get_meta(PTR).has_ops && guf_obj_get_meta(PTR).data.ops->free) { guf_obj_get_meta(PTR).data.ops->free(PTR); }
#define GUF_OBJ_LIFETIME_BLOCK_TYPESAFE(ptr, code_block) \
do { \
code_block \
GUF_OBJ_FREE_TYPESAFE((ptr)); \
} while (0);
#define GUF_OBJ_LIFETIME_BLOCK(ptr, code_block) \
do { \
code_block \
GUF_OBJ_FREE((ptr)); \
} while (0); \
typedef const char* guf_const_cstr_type;
typedef char* guf_cstr_type;
extern const guf_obj_meta guf_cstr_obj_meta;
extern const guf_obj_meta guf_const_cstr_obj_meta;
#endif #endif
#ifndef GUF_OBJ_TYPE
#error "GUF_OBJ_TYPE not set"
#endif
#ifndef GUF_OBJ_OPS_TYPENAME
#define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_OBJ_TYPE, _ops)
#endif
typedef struct GUF_OBJ_OPS_TYPENAME {
GUF_OBJ_TYPE *(*copy_init)(GUF_OBJ_TYPE *dst, const GUF_OBJ_TYPE *src);
GUF_OBJ_TYPE *(*move_init)(GUF_OBJ_TYPE *dst, GUF_OBJ_TYPE *src);
void (*free)(GUF_OBJ_TYPE *obj);
bool (*eq)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b);
int (*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b);
int (*cmp_inv)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b); // Define with GUF_OBJ_OPS_DEFINE_CMP_REVERSE.
} GUF_OBJ_OPS_TYPENAME;
static inline GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_OPS_TYPENAME, _cpy) (GUF_OBJ_TYPE *dst, GUF_OBJ_TYPE *src, const GUF_OBJ_OPS_TYPENAME *ops, guf_obj_cpy_opt cpy_opt)
{
GUF_ASSERT_RELEASE(dst);
GUF_ASSERT_RELEASE(src);
GUF_ASSERT_RELEASE(ops);
if (cpy_opt == GUF_CPY_VALUE) {
dst = memcpy(dst, src, sizeof(GUF_OBJ_TYPE));
GUF_ASSERT_RELEASE(dst); // Should never fail.
} else if (cpy_opt == GUF_CPY_DEEP) {
GUF_ASSERT_RELEASE(ops->copy_init);
dst = ops->copy_init(dst, src);
} else if (cpy_opt == GUF_CPY_MOVE) {
GUF_ASSERT_RELEASE(ops->move_init);
dst = ops->move_init(dst, src);
}
GUF_ASSERT(dst);
return dst;
}
#undef GUF_OBJ_TYPE
#undef GUF_OBJ_OPS_TYPENAME

View File

@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "guf_common.h" #include "guf_common.h"
#include "guf_obj.h" // #include "guf_obj.h"
#define GUF_STR_ABORT_ON_ALLOC_FAILURE 1 #define GUF_STR_ABORT_ON_ALLOC_FAILURE 1

View File

@ -1,9 +1,22 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "guf_cstr.h"
#define GUF_DBUF_INITIAL_CAP 128
#define GUF_CNT_NAME dbuf_int
#define GUF_CNT_T int
#include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_const_cstr
#define GUF_CNT_T guf_const_cstr
#define GUF_CNT_T_OPS guf_const_cstr_operations
#include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_heap_cstr
#define GUF_CNT_T guf_heap_cstr
#define GUF_CNT_T_OPS guf_heap_cstr_operations
#include "guf_dbuf.h" #include "guf_dbuf.h"
#include "guf_str.h"
typedef struct guf_test { typedef struct guf_test {
const char *name, *expected_output; const char *name, *expected_output;
@ -13,81 +26,66 @@ typedef struct guf_test {
bool passed; bool passed;
} guf_test; } guf_test;
static void guf_dbuf_test(guf_test *test)
{
guf_dbuf ints = GUF_DBUF_NEW(int);
for (int i = 0; i < 2048; ++i) {
GUF_DBUF_PUSH_VAL(&ints, int, i);
}
int last_int = GUF_DBUF_LAST_VAL(&ints, int);
int first_int = GUF_DBUF_FIRST_VAL(&ints, int);
int nth = GUF_DBUF_AT_VAL(&ints, int, 14);
printf("First: %d, Last: %d, 14th: %d\n", first_int, last_int, nth);
guf_dbuf strings = GUF_DBUF_NEW(guf_const_cstr_type);
GUF_DBUF_PUSH_VAL(&strings, guf_const_cstr_type, "First elem");
GUF_DBUF_PUSH_VAL(&strings, guf_const_cstr_type, "Second elem");
GUF_DBUF_PUSH_VAL(&strings, guf_const_cstr_type, "Third elem");
int i = 0;
GUF_DBUF_FOREACH(strings, guf_const_cstr_type, elem) {
printf("elem %d: %s\n", i++, *elem);
}
guf_dbuf mut_strings = guf_dbuf_new(guf_cstr_obj_meta);
char foo[] = "Hello, world!";
char bar[] = "Hej, verden!";
char *baz = calloc(64, sizeof(char));
strcpy(baz, "Farvel, I forbandede hundehoveder!");
GUF_DBUF_PUSH_VAL_CPY(&mut_strings, guf_cstr_type, &foo[0]);
GUF_DBUF_PUSH_VAL_CPY(&mut_strings, guf_cstr_type, &bar[0]);
guf_dbuf_push(&mut_strings, &baz, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(baz == NULL);
// char *popped = GUF_DBUF_POP_VAL(&mut_strings, cstr_type);
// printf("popped: %s\n", popped);
// free(popped);
printf("First str_mut: %s, second: %s, last: %s\n", GUF_DBUF_FIRST_VAL(&mut_strings, guf_cstr_type), GUF_DBUF_AT_VAL(&mut_strings, guf_cstr_type, 1), GUF_DBUF_LAST_VAL(&mut_strings, guf_cstr_type));
guf_dbuf_free(&mut_strings);
}
int main(void) int main(void)
{ {
bool success = true; bool success = true;
guf_dbuf_test(NULL); dbuf_int integers = dbuf_int_new();
// guf_test_arr_register(); dbuf_int_push_val(&integers, 420);
printf("initial cap %td\n", integers.capacity);
dbuf_int_push_val(&integers, 520);
dbuf_int_push_val(&integers, 620);
dbuf_int_push_val(&integers, 720);
// bool alloc_init = guf_alloc_init(); int i = 0;
// GUF_ASSERT_RELEASE(alloc_init); GUF_DBUF_FOREACH(integers, int, elem) {
printf("elem %d: %d\n", i, *elem);
++i;
}
// void *ptr = guf_malloc(sizeof(int) * 42, "int alloc"); GUF_FOREACH(&integers, dbuf_int, it) {
printf("it-elem: %d", *it.cur);
if (it.next(&it, 1).cur != it.end) {
printf(", it-next: %d", *it.next(&it, 1).cur);
}
if (it.next(&it, -1).cur != it.end) {
printf(", it-prev: %d", *it.next(&it, -1).cur);
}
printf("\n");
}
// void *ptr2 = guf_malloc(sizeof(int) * 4, "int alloc 2 "); for (dbuf_int_iter it = dbuf_int_begin(&integers); it.cur != it.end; it = it.next(&it, 2)) {
printf("every other: %d\n", *it.cur);
}
// void *ptr3 = guf_malloc(sizeof(int) * 4, "int alloc 3aaaaaaaaaafsjfjsdkfjksldjflssdfsdfjjjsdflkdjflsd"); dbuf_const_cstr strings = dbuf_const_cstr_new();
dbuf_const_cstr_push_val(&strings, "First");
printf("initial cap %td\n", strings.capacity);
// guf_free(ptr, "int alloc"); dbuf_const_cstr_push_val(&strings, "Second");
// guf_free(ptr3, "int alloc 3"); guf_const_cstr foo = "Hello, World!";
// guf_free(ptr2, "int alloc 2"); dbuf_const_cstr_push(&strings, &foo, GUF_CPY_VALUE);
// guf_alloc_print(); GUF_FOREACH(&strings, dbuf_const_cstr, it) {
printf("str: %s\n", *it.cur);
}
// GUF_ARR_FOREACH(test_arr, guf_test, test) { dbuf_heap_cstr mut_strings = dbuf_heap_cstr_new();
// test->test_fn(test); dbuf_heap_cstr_push_val_cpy(&mut_strings, "First mut!");
// if (guf_str_equals(&test->expected_output, &test->output)) { dbuf_heap_cstr_push_val_cpy(&mut_strings, "Second mut!");
// printf("Test %s passed!\n", guf_str_get_const_c_str(&test->name));
// } else { char *move_me_pls = calloc(128, sizeof(char));
// printf("Test %s failed!\n", guf_str_get_const_c_str(&test->name)); strcpy(move_me_pls, "Third mut");
// } dbuf_heap_cstr_push(&mut_strings, &move_me_pls, GUF_CPY_MOVE);
// } GUF_ASSERT_RELEASE(move_me_pls == NULL);
GUF_FOREACH(&mut_strings, dbuf_heap_cstr, it) {
printf("str: %s\n", *it.cur);
}
dbuf_heap_cstr_free(&mut_strings);
dbuf_const_cstr_free(&strings);
dbuf_int_free(&integers);
return success ? EXIT_SUCCESS : EXIT_FAILURE; return success ? EXIT_SUCCESS : EXIT_FAILURE;
} }