Use macro templates instead of void pointers
This commit is contained in:
parent
0751726fc5
commit
9e0da64cb4
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
177
src/guf_common.c
177
src/guf_common.c
@ -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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@ -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
63
src/guf_common_utils.h
Normal 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
92
src/guf_cstr.h
Normal 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
|
||||||
234
src/guf_darr.h
234
src/guf_darr.h
@ -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
|
|
||||||
|
|
||||||
260
src/guf_dbuf.c
260
src/guf_dbuf.c
@ -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;
|
|
||||||
}
|
|
||||||
420
src/guf_dbuf.h
420
src/guf_dbuf.h
@ -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
|
||||||
@ -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
59
src/guf_hash.h
Normal 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
|
||||||
@ -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)
|
|
||||||
};
|
|
||||||
167
src/guf_obj.h
167
src/guf_obj.h
@ -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
|
||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
128
src/guf_test.c
128
src/guf_test.c
@ -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;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user