Rewrite dbuf

This commit is contained in:
jun 2025-01-27 08:42:21 +01:00
parent be2daf72fd
commit c9b18a220e
11 changed files with 885 additions and 839 deletions

91
src/guf_alloc.h Normal file
View File

@ -0,0 +1,91 @@
#ifndef GUF_ALLOC_H
#define GUF_ALLOC_H
#include <stdlib.h>
#include <stddef.h>
#include "guf_common.h"
// A Custom allocator interface as described in https://nullprogram.com/blog/2023/12/17/ (last-retrieved 2025-01-25)
typedef struct guf_allocator {
void *(*alloc)(ptrdiff_t size, void *ctx);
void *(*realloc)(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx);
void (*free)(void *ptr, ptrdiff_t size, void *ctx);
void *ctx;
} guf_allocator;
// register_alloc_type(const char *typestr) (starting from 1)
// lookup_alloc_typename(ptrdiff_t type_id)
// pass CNT_T_TYPE_ID to containers
// type_id = 0 "other"
// keeping track of (heap?) allocs: alloc_map[type_id] = {.size_alloced, .size_freed}
#define GUF_ALLOC_ENABLE_TRACKING
typedef struct guf_alloc_libc_ctx {
bool zero_init;
size_t alloc_id;
// dbuf_alloc_info
} guf_alloc_libc_ctx;
static inline void *guf_alloc_libc(ptrdiff_t size, void *ctx)
{
GUF_ASSERT_RELEASE(size >= 0);
guf_alloc_libc_ctx *alloc_ctx = (guf_alloc_libc_ctx*)ctx;
if (size == 0) {
return NULL;
} else if (alloc_ctx && alloc_ctx->zero_init) {
return calloc(size, 1);
} else {
return malloc(size);
}
}
static inline void *guf_realloc_libc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx)
{
GUF_ASSERT_RELEASE(ptr);
GUF_ASSERT_RELEASE(old_size >= 0 && new_size >= 0);
guf_alloc_libc_ctx *alloc_ctx = (guf_alloc_libc_ctx*)ctx;
if (old_size == new_size) {
return ptr;
}
void *new_ptr = realloc(ptr, new_size);
if (!new_ptr || new_size == 0) {
return NULL;
} else if (alloc_ctx && alloc_ctx->zero_init && new_size > old_size) {
ptrdiff_t len = new_size - old_size;
GUF_ASSERT(len > 0);
GUF_ASSERT(old_size + len == new_size);
memset((char*)ptr + old_size, 0, len); // TODO: sketchy
}
return new_ptr;
}
static inline void guf_free_libc(void *ptr, ptrdiff_t size, void *ctx)
{
(void)ctx;
(void)size;
free(ptr);
}
static guf_allocator guf_allocator_libc = {
.alloc = guf_alloc_libc,
.realloc = guf_realloc_libc,
.free = guf_free_libc,
.ctx = NULL
};
// typedef struct guf_alloc_info {
// const char *typename;
// ptrdiff_t allocated_bytes, freed_bytes;
// ptrdiff_t alloc_count, free_count;
// } guf_alloc_info;
// // define in other file
// typedef struct guf_alloc_tracker_ctx {
// // dbuf_alloc_info alloc_info_buf;
// } guf_alloc_tracker_ctx;
#endif

View File

@ -5,7 +5,7 @@
#include <assert.h>
#include "guf_common_utils.h"
typedef enum guf_err_type {
typedef enum guf_err {
GUF_ERR_NONE = 0,
GUF_ERR_UNSPECIFIED,
GUF_ERR_ALLOC_FAIL,
@ -17,18 +17,12 @@ typedef enum guf_err_type {
GUF_ERR_INVALID_ARG,
GUF_ERR_RUNTIME,
GUF_ERR_LOGIC,
GUF_ERR_RELEASE_ASSERT,
GUF_ERR_ASSERT_FAIL,
GUF_ERR_TYPES_NUM
} guf_err_type;
typedef struct guf_err {
guf_err_type type;
const char *msg;
} guf_err;
static const guf_err GUF_SUCCESS = (guf_err){.type = GUF_ERR_NONE, .msg = NULL};
typedef void(*guf_panic_handler_fn)(const guf_err *err);
typedef void(*guf_panic_handler_fn)(guf_err err, const char *msg);
extern guf_panic_handler_fn guf_global_panic_handler;
extern const char *guf_err_type_str[GUF_ERR_TYPES_NUM];
@ -38,6 +32,10 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl
guf_global_panic_handler = panic_handler;
}
#define GUF_FILE_LINE_STR() "file '" __FILE__ "' line " GUF_STRINGIFY(__LINE__)
#define GUF_ERR_MSG(msg) msg " (" GUF_FILE_LINE_STR() ")"
#define GUF_ERR_MSG_EMPTY() "(" GUF_FILE_LINE_STR() ")"
// Break on debug, cf. https://nullprogram.com/blog/2022/06/26/ (last retrieved 2025-01-07)
#if __GNUC__ || __clang__
#define GUF_DEBUG_BREAK_CODE __builtin_trap()
@ -50,8 +48,7 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl
#ifndef NDEBUG
#define GUF_ASSERT(COND) do { \
if (!(COND)) { \
fputs("libguf assertion '" #COND "' failed (" GUF_FILE_LINE_STR() ")\n", stderr); \
GUF_DEBUG_BREAK_CODE; \
guf_global_panic_handler(GUF_ERR_ASSERT_FAIL, "(assertion '" #COND "' " GUF_FILE_LINE_STR() ")"); \
abort(); \
}\
} while(0);
@ -61,43 +58,67 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl
#define GUF_ASSERT_RELEASE(COND) do { \
if (!(COND)) { \
guf_err guf_assert_err = (guf_err){.type = GUF_ERR_RELEASE_ASSERT, .msg = "(assertion '" #COND "' in file " __FILE__ " on line " GUF_STRINGIFY(__LINE__) ")"};\
guf_global_panic_handler(&guf_assert_err);\
guf_global_panic_handler(GUF_ERR_ASSERT_FAIL, "(release assertion '" #COND "' " GUF_FILE_LINE_STR() ")"); \
abort();\
} \
} while (0);
#define GUF_FILE_LINE_STR() "file '" __FILE__ "' line " GUF_STRINGIFY(__LINE__)
#define GUF_ERR_MSG(msg) msg " (" GUF_FILE_LINE_STR() ")"
#define GUF_PANIC(err) do {guf_global_panic_handler(err, "(" GUF_FILE_LINE_STR() ")")} while(0);
#define GUF_PANIC(error_type) do {guf_err guf_panic_err = (guf_err){.type = (error_type), .msg = "(" GUF_FILE_LINE_STR() ")"}; guf_global_panic_handler(&guf_panic_err)} while(0);
static inline bool guf_is_err(const guf_err *err)
static inline void guf_panic_handler_default(guf_err err, const char *msg)
{
GUF_ASSERT_RELEASE(err);
GUF_ASSERT_RELEASE(err->type >= 0 && err->type < GUF_ERR_TYPES_NUM);
return err->type != GUF_ERR_NONE;
fputs("libguf panic", stderr);
if (err > 0 && err < GUF_ERR_TYPES_NUM) {
fputs(": ", stderr);
GUF_ASSERT(GUF_STATIC_BUF_SIZE(guf_err_type_str) == GUF_ERR_TYPES_NUM);
fputs(guf_err_type_str[err], stderr);
fputc(' ', stderr);
if (msg && err != GUF_ERR_NONE) {
fputs(msg, stderr);
}
} else {
fputs(" (note: err passed to panic is invalid)", stderr);
}
fputc('\n', stderr);
#ifndef NDEBUG
GUF_DEBUG_BREAK_CODE;
#endif
abort();
}
static inline void guf_panic_handler_default(const guf_err *err)
static inline void guf_panic(guf_err err, const char *msg)
{
if (!err) {
fputs("libguf panic!", stderr);
GUF_ASSERT(guf_global_panic_handler);
if (!guf_global_panic_handler) {
fputs("libguf panic (note: guf_global_panic_handler is NULL)\n", stderr);
if (msg) {
fputs(": ", stderr);
fputs(msg, stderr);
}
abort();
}
guf_global_panic_handler(err, msg);
abort(); // Just in case...
}
fputs("libguf panic: ", stderr);
if (err->type > 0 || err->type < GUF_ERR_TYPES_NUM) {
GUF_ASSERT(GUF_STATIC_BUF_SIZE(guf_err_type_str) == GUF_ERR_TYPES_NUM);
fputs(guf_err_type_str[err->type], stderr);
fputc(' ', stderr);
if (err->msg && err->type != GUF_ERR_NONE) {
fputs(err->msg, stderr);
}
static inline void guf_err_set_or_panic(guf_err *err, guf_err err_val, const char *panic_msg)
{
if (!err) {
guf_panic(err_val, panic_msg);
} else {
*err = err_val;
}
}
fputc('\n', stderr);
abort();
static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
{
if (err) {
*err = err_val;
}
}
#ifdef GUF_IMPLEMENTATION
@ -115,20 +136,8 @@ static inline void guf_panic_handler_default(const guf_err *err)
[GUF_ERR_INVALID_ARG] = "Invalid argument",
[GUF_ERR_RUNTIME] = "Runtime error",
[GUF_ERR_LOGIC] = "Logic error",
[GUF_ERR_RELEASE_ASSERT] = "Release assertion failed"
[GUF_ERR_ASSERT_FAIL] = "Assertion failed"
};
#endif
static inline void guf_panic(const guf_err *err)
{
GUF_ASSERT(guf_global_panic_handler);
if (!guf_global_panic_handler) {
fputs("libguf panic (note: guf_global_panic_handler is NULL)\n", stderr);
abort();
}
guf_global_panic_handler(err);
abort(); // Just in case...
}
#endif

View File

@ -6,10 +6,8 @@
#include <stdio.h>
#include <assert.h>
#include "guf_common_utils.h"
#include "guf_assert.h"
#include "guf_int.h"
#include "guf_hash.h"
#include "guf_common_utils.h"
typedef enum guf_obj_cpy_opt {
GUF_CPY_VALUE = 0,
@ -22,11 +20,37 @@ typedef enum guf_sort_opt {
GUF_SORT_DESCENDING = 1
} guf_sort_opt;
// 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);
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;
}
if (guf_is_mul_overflow_size_t(count, sizeof_elem)) {
return false;
}
size_t size = (size_t)count * (size_t)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;
}
#endif

View File

@ -2,6 +2,13 @@
#define GUF_COMMON_UTILS_H
#include <limits.h>
#define GUF_SWAP(TYPE, val_a, val_b) do {TYPE guf_swap_tmp = val_a; val_a = val_b; val_b = guf_swap_tmp;} while (0);
#define GUF_STATIC_BUF_SIZE(BUF) (sizeof((BUF)) / (sizeof((BUF)[0])))
#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))
// 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)
@ -9,9 +16,8 @@
#define GUF_TOK_STRINGIFY(x) #x
#define GUF_STRINGIFY(x) GUF_TOK_STRINGIFY(x)
#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_CNT_FOREACH(CNT_PTR, CNT_TYPE, IT_NAME) for (GUFCAT(CNT_TYPE, _iter) IT_NAME = GUFCAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.ptr != GUFCAT(CNT_TYPE, _end)(CNT_PTR).ptr; it = GUFCAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, 1))
#define GUF_CNT_FOREACH_REVERSE(CNT_PTR, CNT_TYPE, IT_NAME) for (GUFCAT(CNT_TYPE, _iter) IT_NAME = GUFCAT(CNT_TYPE, _rbegin)(CNT_PTR); IT_NAME.ptr != GUFCAT(CNT_TYPE, _rend)(CNT_PTR).ptr; it = GUFCAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, 1))
#define GUF_LIFETIME_BLOCK(obj_init_code, obj_name, free_fn, code) do { \
obj_init_code; \

View File

@ -4,15 +4,25 @@
#include "guf_common.h"
typedef const char* guf_const_cstr;
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 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);
}
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)
static inline guf_heap_cstr *guf_heap_cstr_copy_construct(guf_heap_cstr *dst, const guf_heap_cstr *src)
{
GUF_ASSERT_RELEASE(dst && src);
if (*src == NULL) {
@ -24,7 +34,7 @@ static inline guf_heap_cstr *guf_heap_cstr_cpy_init(guf_heap_cstr *dst, const gu
return dst;
}
static inline guf_heap_cstr *guf_heap_cstr_move_init(guf_heap_cstr *dst, guf_heap_cstr *src)
static inline guf_heap_cstr *guf_heap_cstr_move_construct(guf_heap_cstr *dst, guf_heap_cstr *src)
{
GUF_ASSERT_RELEASE(dst && src);
*dst = *src;
@ -53,45 +63,4 @@ static inline bool guf_heap_cstr_eq(const guf_heap_cstr *a, const guf_heap_cstr
return 0 == strcmp(*a, *b);
}
static inline GUF_OBJ_OPS_DEFINE_CMP_VOID(guf_heap_cstr, guf_heap_cstr_cmp)
static inline GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(guf_heap_cstr, guf_heap_cstr_cmp)
static guf_heap_cstr_ops_type guf_heap_cstr_ops = {
.copy_init = guf_heap_cstr_cpy_init,
.move_init = guf_heap_cstr_move_init,
.free = guf_heap_cstr_free,
.eq = guf_heap_cstr_eq,
.cmp = guf_heap_cstr_cmp,
.cmp_void = guf_heap_cstr_cmp_void,
.cmp_void_inv = guf_heap_cstr_cmp_void_inv
};
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 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 inline GUF_OBJ_OPS_DEFINE_CMP_VOID(guf_const_cstr, guf_const_cstr_cmp)
static inline GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(guf_const_cstr, guf_const_cstr_cmp)
static guf_const_cstr_ops_type guf_const_cstr_ops = {
.default_value = NULL,
.copy_init = NULL,
.move_init = NULL,
.free = NULL,
.eq = guf_const_cstr_eq,
.cmp = guf_const_cstr_cmp,
.cmp_void = guf_const_cstr_cmp_void,
.cmp_void_inv = guf_const_cstr_cmp_void_inv
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,12 @@
#define GUF_INT_H
#include "guf_common.h"
#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))
#define GUF_DEFINE_MIN_MAX_CLAMP(int_type, int_type_name)\
static inline int_type GUFCAT(guf_min_, int_type_name)(int_type a, int_type b) {return a >= b ? a : b;}\
static inline int_type GUFCAT(guf_max_, int_type_name)(int_type a, int_type b) {return a >= b ? a : b;}\
static inline int_type GUFCAT(guf_clamp_, int_type_name)(int_type x, int_type min, int_type max) {return GUFCAT(guf_max_, int_type_name)(GUFCAT(guf_min_, int_type_name)(x, max), min);}
GUF_DEFINE_MIN_MAX_CLAMP(char, char)
GUF_DEFINE_MIN_MAX_CLAMP(int, int)
GUF_DEFINE_MIN_MAX_CLAMP(short, short)
GUF_DEFINE_MIN_MAX_CLAMP(long, long)
@ -19,22 +16,22 @@ GUF_DEFINE_MIN_MAX_CLAMP(int8_t, i8)
GUF_DEFINE_MIN_MAX_CLAMP(int16_t, i16)
GUF_DEFINE_MIN_MAX_CLAMP(int32_t, i32)
GUF_DEFINE_MIN_MAX_CLAMP(int64_t, i64)
GUF_DEFINE_MIN_MAX_CLAMP(ptrdiff_t, ptrdiff_t)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned char, unsigned_char)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned, unsigned)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned short, unsigned_short)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned short, u_short)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned long, unsigned_long)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned long long, unsigned_long_long)
GUF_DEFINE_MIN_MAX_CLAMP(uint8_t, u8)
GUF_DEFINE_MIN_MAX_CLAMP(uint16_t, u16)
GUF_DEFINE_MIN_MAX_CLAMP(uint32_t, u32)
GUF_DEFINE_MIN_MAX_CLAMP(uint64_t, u64)
GUF_DEFINE_MIN_MAX_CLAMP(char, guf_char)
GUF_DEFINE_MIN_MAX_CLAMP(unsigned char, guf_unsigned_char)
GUF_DEFINE_MIN_MAX_CLAMP(size_t, size_t)
#undef GUF_DEFINE_MIN_MAX_CLAMP
static inline int guf_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN ); return -x;}
static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN ); return -x;} // I would not drink that...
static inline long guf_abs_long(long x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > LONG_MIN ); return -x;}
static inline long long guf_abs_long_long(long long x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > LLONG_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;}
@ -43,231 +40,4 @@ static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {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 ptrdiff_t guf_abs_ptrdiff(ptrdiff_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > PTRDIFF_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;
}
if (guf_is_mul_overflow_size_t(count, sizeof_elem)) {
return false;
}
size_t size = (size_t)count * (size_t)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;
}
// Signed integer types:
#define GUF_OBJ_TYPE int
#define GUF_OBJ_OPS_TYPENAME guf_int_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE short
#define GUF_OBJ_OPS_TYPENAME guf_short_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE long
#define GUF_OBJ_OPS_TYPENAME guf_long_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE long long
#define GUF_OBJ_OPS_TYPENAME guf_long_long_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE int8_t
#define GUF_OBJ_OPS_TYPENAME guf_int8_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE int16_t
#define GUF_OBJ_OPS_TYPENAME guf_int16_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE int32_t
#define GUF_OBJ_OPS_TYPENAME guf_int32_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE int64_t
#define GUF_OBJ_OPS_TYPENAME guf_int64_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE ptrdiff_t
#define GUF_OBJ_OPS_TYPENAME guf_ptrdiff_t_ops_type
#include "guf_obj.h"
// Unsigned integer types:
#define GUF_OBJ_TYPE unsigned
#define GUF_OBJ_OPS_TYPENAME guf_unsigned_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE unsigned short
#define GUF_OBJ_OPS_TYPENAME guf_unsigned_short_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE unsigned long
#define GUF_OBJ_OPS_TYPENAME guf_unsigned_long_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE unsigned long long
#define GUF_OBJ_OPS_TYPENAME guf_unsigned_long_long_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE uint8_t
#define GUF_OBJ_OPS_TYPENAME guf_uint8_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE uint16_t
#define GUF_OBJ_OPS_TYPENAME guf_uint16_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE uint32_t
#define GUF_OBJ_OPS_TYPENAME guf_uint32_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE uint64_t
#define GUF_OBJ_OPS_TYPENAME guf_uint64_t_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE size_t
#define GUF_OBJ_OPS_TYPENAME guf_size_t_ops_type
#include "guf_obj.h"
// Signed/Unsigned character types:
#define GUF_OBJ_TYPE char
#define GUF_OBJ_OPS_TYPENAME guf_char_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE unsigned char
#define GUF_OBJ_OPS_TYPENAME guf_unsigned_char_ops_type
#include "guf_obj.h"
// Floating point:
#define GUF_OBJ_TYPE float
#define GUF_OBJ_OPS_TYPENAME guf_float_ops_type
#include "guf_obj.h"
#define GUF_OBJ_TYPE double
#define GUF_OBJ_OPS_TYPENAME guf_double_ops_type
#include "guf_obj.h"
extern const guf_int_ops_type guf_int_ops;
extern const guf_short_ops_type guf_short_ops;
extern const guf_long_ops_type guf_long_ops;
extern const guf_long_long_ops_type guf_long_long_ops;
extern const guf_ptrdiff_t_ops_type guf_ptrdiff_t_ops;
extern const guf_int8_t_ops_type guf_int8_t_ops;
extern const guf_int16_t_ops_type guf_int16_t_ops;
extern const guf_int32_t_ops_type guf_int32_t_ops;
extern const guf_int64_t_ops_type guf_int64_t_ops;
extern const guf_unsigned_ops_type guf_unsigned_ops;
extern const guf_unsigned_short_ops_type guf_unsigned_short_ops;
extern const guf_unsigned_long_ops_type guf_unsigned_long_ops;
extern const guf_unsigned_long_long_ops_type guf_unsigned_long_long_ops;
extern const guf_size_t_ops_type guf_size_t_ops;
extern const guf_uint8_t_ops_type guf_uint8_t_ops;
extern const guf_uint16_t_ops_type guf_uint16_t_ops;
extern const guf_uint32_t_ops_type guf_uint32_t_ops;
extern const guf_uint64_t_ops_type guf_uint64_t_ops;
extern const guf_char_ops_type guf_char_ops;
extern const guf_unsigned_char_ops_type guf_unsigned_char_ops;
extern const guf_float_ops_type guf_float_ops;
extern const guf_double_ops_type guf_double_ops;
#ifdef GUF_IMPLEMENTATION
#define GUF_DEFINE_INTEGER_CMP(int_type, fn_name) \
int fn_name(const int_type *a, const int_type *b) {GUF_ASSERT(a && b); return (*a == *b ? 0 : *a < *b ? -1 : 1);} \
GUF_OBJ_OPS_DEFINE_CMP_VOID(int_type, fn_name) \
GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(int_type, fn_name)
#define GUF_DEFINE_INTEGER_OPS(int_type)\
const GUFCAT(int_type, _ops_type) GUFCAT(int_type, _ops) = {\
.cmp = GUFCAT(int_type, _cmp),\
.cmp_void = GUFCAT(int_type, _cmp_void), \
.cmp_void_inv = GUFCAT(int_type, _cmp_void_inv), \
.default_value = 0,\
.eq = NULL, \
.copy_init = NULL,\
.move_init = NULL,\
.free = NULL\
};\
GUF_DEFINE_INTEGER_CMP(int, guf_int_cmp)
GUF_DEFINE_INTEGER_CMP(short, guf_short_cmp)
GUF_DEFINE_INTEGER_CMP(long, guf_long_cmp)
GUF_DEFINE_INTEGER_CMP(long long, guf_long_long_cmp)
GUF_DEFINE_INTEGER_CMP(int8_t, guf_int8_t_cmp)
GUF_DEFINE_INTEGER_CMP(int16_t, guf_int16_t_cmp)
GUF_DEFINE_INTEGER_CMP(int32_t, guf_int32_t_cmp)
GUF_DEFINE_INTEGER_CMP(int64_t, guf_int64_t_cmp)
GUF_DEFINE_INTEGER_CMP(ptrdiff_t, guf_ptrdiff_t_cmp)
GUF_DEFINE_INTEGER_CMP(unsigned, guf_unsigned_cmp)
GUF_DEFINE_INTEGER_CMP(unsigned short, guf_unsigned_short_cmp)
GUF_DEFINE_INTEGER_CMP(unsigned long, guf_unsigned_long_cmp)
GUF_DEFINE_INTEGER_CMP(unsigned long long, guf_unsigned_long_long_cmp)
GUF_DEFINE_INTEGER_CMP(uint8_t, guf_uint8_t_cmp)
GUF_DEFINE_INTEGER_CMP(uint16_t, guf_uint16_t_cmp)
GUF_DEFINE_INTEGER_CMP(uint32_t, guf_uint32_t_cmp)
GUF_DEFINE_INTEGER_CMP(uint64_t, guf_uint64_t_cmp)
GUF_DEFINE_INTEGER_CMP(size_t, guf_size_t_cmp)
GUF_DEFINE_INTEGER_CMP(char, guf_char_cmp)
GUF_DEFINE_INTEGER_CMP(unsigned char, guf_unsigned_char_cmp)
GUF_DEFINE_INTEGER_CMP(float, guf_float_cmp)
GUF_DEFINE_INTEGER_CMP(double, guf_double_cmp)
GUF_DEFINE_INTEGER_OPS(guf_int)
GUF_DEFINE_INTEGER_OPS(guf_short)
GUF_DEFINE_INTEGER_OPS(guf_long_long)
GUF_DEFINE_INTEGER_OPS(guf_int8_t)
GUF_DEFINE_INTEGER_OPS(guf_int16_t)
GUF_DEFINE_INTEGER_OPS(guf_int32_t)
GUF_DEFINE_INTEGER_OPS(guf_int64_t)
GUF_DEFINE_INTEGER_OPS(guf_unsigned)
GUF_DEFINE_INTEGER_OPS(guf_unsigned_short)
GUF_DEFINE_INTEGER_OPS(guf_unsigned_long_long)
GUF_DEFINE_INTEGER_OPS(guf_uint8_t)
GUF_DEFINE_INTEGER_OPS(guf_uint16_t)
GUF_DEFINE_INTEGER_OPS(guf_uint32_t)
GUF_DEFINE_INTEGER_OPS(guf_uint64_t)
GUF_DEFINE_INTEGER_OPS(guf_char)
GUF_DEFINE_INTEGER_OPS(guf_unsigned_char)
GUF_DEFINE_INTEGER_OPS(guf_float)
GUF_DEFINE_INTEGER_OPS(guf_double)
#undef GUF_DEFINE_INTEGER_OPS
#undef GUF_DEFINE_INTEGER_CMP
#endif
#endif

View File

@ -2,30 +2,6 @@
#define GUF_OBJ_H
#include <string.h>
#include "guf_common.h"
#define GUF_OBJ_OPS_INTEGRAL_TYPE(obj_ops_type, eq, cmp, cmp_void, cmp_void_inv) (obj_ops_type){.copy_init = NULL, .move_init = NULL, .free = NULL, .eq = eq, .cmp = cmp, .cmp_void = cmp_void, .cmp_void_inv = cmp_void_inv}
#define GUF_OBJ_OPS_EMPTY(obj_ops_type) (obj_ops_type){.copy_init = NULL, .move_init = NULL, .free = NULL, .eq = NULL, .cmp = NULL, .cmp_void = NULL, .cmp_void_inv = NULL}
#define GUF_OBJ_OPS_DEFINE_CMP_VOID(obj_type, cmp_fn) int GUFCAT(cmp_fn, _void)(const void *a, const void *b) {return cmp_fn((const obj_type*)a, (const obj_type*)b);}
#define GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(obj_type, cmp_fn) int GUFCAT(cmp_fn, _void_inv)(const void *a, const void *b) {return -cmp_fn((const obj_type*)a, (const obj_type*)b);}
#define GUF_OBJ_CPY(obj_type, obj_ops, dst_ptr, src_ptr, cpy_opts, success_bool_ptr) do { \
obj_type *obj_cpy_dst_ptr = dst_ptr;\
obj_type *obj_cpy_src_ptr = src_ptr;\
if (cpy_opt == GUF_CPY_VALUE) {\
*obj_cpy_dst_ptr = *obj_cpy_src_ptr;\
*success_bool_ptr = true;\
} else if (cpy_opt == GUF_CPY_DEEP) {\
GUF_ASSERT_RELEASE(obj_ops.copy_init);\
if (obj_ops.copy_init(obj_cpy_dst_ptr, obj_cpy_src_ptr)) {*success_bool_ptr = true;} else {*success_bool_ptr = false;}\
} else if (cpy_opt == GUF_CPY_MOVE) {\
GUF_ASSERT_RELEASE(obj_ops.move_init);\
if (obj_ops.move_init(obj_cpy_dst_ptr, obj_cpy_src_ptr)) {*success_bool_ptr = true;} else {*success_bool_ptr = false;}\
} else {\
GUF_ASSERT_RELEASE(false);\
}\
} while (0);\
#endif
/*
@ -36,28 +12,26 @@
- GUF_OBJ_OPS_TYPENAME: The typename of the operations struct for the object (default: GUFCAT(GUF_OBJ_TYPE, _ops))
*/
#ifndef GUF_OBJ_TYPE
#error "GUF_OBJ_TYPE not set"
#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_type)
#endif
// #ifndef GUF_OBJ_OPS_TYPENAME
// #define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_OBJ_TYPE, _ops_type)
// #endif
// #ifndef GUF_OBJ_OPS_NAME
// #define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_OBJ_TYPE, _ops)
// #endif
typedef struct GUF_OBJ_OPS_TYPENAME {
GUF_OBJ_TYPE default_value;
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_void)(const void *a, const void *b);
int (*cmp_void_inv)(const void *a, const void *b);
} GUF_OBJ_OPS_TYPENAME;
// typedef struct GUF_OBJ_OPS_TYPENAME {
// GUF_OBJ_TYPE *(*copy)(GUF_OBJ_TYPE *dst, const GUF_OBJ_TYPE *src);
// GUF_OBJ_TYPE *(*move)(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); // a < b -> -1; a == b -> 0; a > b -> 1
// guf_hash_size_t (*hash)(const GUF_OBJ_TYPE *obj);
// } GUF_OBJ_OPS_TYPENAME;
#undef GUF_OBJ_TYPE
#undef GUF_OBJ_OPS_TYPENAME

160
src/guf_sort.h Normal file
View File

@ -0,0 +1,160 @@
#ifndef GUF_SORT_H
#define GUF_SORT_H
#include <stddef.h>
#include "guf_common.h"
#endif
#ifndef GUF_OBJ_TYPE
#error "GUF_OBJ_TYPE not set"
#endif
#ifndef GUF_OBJ_TYPE_NAME
#define GUF_OBJ_TYPE_NAME GUF_OBJ_TYPE
#endif
#define GUF_FN_KEYWORDS static inline
#ifdef GUF_DECLARATIONS_ONLY
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b));
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _merge_sort)(GUF_OBJ_TYPE *arr, GUF_OBJ_TYPE *arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b));
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b))
#else
#define guf_before(a_ptr, b_ptr) (cmp ? (sort_opt == GUF_SORT_ASCENDING ? 1 : -1) * cmp(a_ptr, b_ptr) == -1 : (sort_opt == GUF_SORT_ASCENDING) ? *(a_ptr) < *(b_ptr) : *(a_ptr) > *(b_ptr))
#define guf_before_or_equal(a_ptr, b_ptr) (cmp ? (sort_opt == GUF_SORT_ASCENDING ? 1 : -1) * cmp(a_ptr, b_ptr) <= 0 : (sort_opt == GUF_SORT_ASCENDING) ? *(a_ptr) <= *(b_ptr) : *(a_ptr) >= *(b_ptr))
/*
Insertion sort.
- stable: yes
- time: O(n^2) (worst, average, and best)
- space: O(1)
*/
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b))
{
GUF_ASSERT_RELEASE(arr);
GUF_ASSERT_RELEASE(n >= 0);
GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING);
for (ptrdiff_t i = 1; i < n; ++i) { // Range [0, i) is sorted.
ptrdiff_t j = i;
while (j > 0 && guf_before(&arr[j], &arr[j - 1])) {
GUF_SWAP(GUF_OBJ_TYPE, arr[j], arr[j - 1]);
j = j - 1;
}
}
return arr;
}
/*
Iterative bottom-up merge-sort: cf. https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation (last-retrieved: 2025-01-26)
- stable: yes
- time: O(n * log n) (worst, average, and best)
- space: always O(n) (for arr_tmp)
*/
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _merge_sort)(GUF_OBJ_TYPE *arr, GUF_OBJ_TYPE *arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b))
{
GUF_ASSERT_RELEASE(arr);
GUF_ASSERT_RELEASE(n >= 0);
GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING);
GUF_OBJ_TYPE *in = arr;
GUF_OBJ_TYPE *out = arr_tmp;
size_t arr_len = n;
for (size_t len = 1; len < arr_len; len = (len * 2 > len) ? 2 * len : SIZE_T_MAX) { // Subarray len 1, 2, 4, 8, ...
for (size_t i = 0; i < arr_len; i = ((i + 2 * len) > i) ? (i + 2 * len) : SIZE_T_MAX) { // For each pair of subarrays of length len:
const size_t left_begin = i; // left subarray: [left_begin, right_begin)
const size_t right_begin = GUF_MIN(i + len, arr_len), right_end = GUF_MIN(i + 2 * len, arr_len); // right subarray [right_begin, right_end)
size_t left_idx = left_begin, right_idx = right_begin;
for (size_t idx = left_begin; idx < right_end; ++idx) { // Merge the left and right subarrays into arr_tmp.
if (left_idx < right_begin && (right_idx >= right_end || guf_before_or_equal(&in[left_idx], &in[right_idx]))) {
out[idx] = in[left_idx++];
} else {
GUF_ASSERT(right_idx < right_end);
out[idx] = in[right_idx++];
}
}
}
GUF_SWAP(GUF_OBJ_TYPE*, in, out); // Avoid copying memory by switching pointers.
}
if (in != arr) {
memcpy(arr, in, sizeof(GUF_OBJ_TYPE) * arr_len);
}
return arr;
}
/*
Quicksort implementation with "Median-of-3 partition" (pp. 188-189) as described in CLRS (plus potential bugs introduced by me).
cf. Cormen; Leiserson; Riverst; Stein (2009). "II.7 Quicksort". In "Introduction to Algorithms" (3rd ed, pp. 170-190). MIT Press
- stable: no
- time: worst O(n^2), average O(n * log n), best O(n * log n)
- space: worst-case O(n * log n) (recursion uses stack space for the function parameters etc.)
*/
static inline GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(GUF_OBJ_TYPE *arr, ptrdiff_t first_idx, ptrdiff_t last_idx, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b))
{
GUF_ASSERT(arr);
GUF_ASSERT(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING);
while (first_idx >= 0 && first_idx < last_idx) {
const ptrdiff_t mid_idx = first_idx + ((last_idx - first_idx) / 2); // Equivalent to (first_idx + last_idx) / 2, cf. https://research.google/blog/extra-extra-read-all-about-it-nearly-all-binary-searches-and-mergesorts-are-broken/ (last-retrieved 2025-01-24)
GUF_ASSERT(mid_idx >= 0 && mid_idx <= last_idx);
// 1.) Partition
ptrdiff_t pivot_idx = last_idx;
if (guf_before(&arr[mid_idx], &arr[first_idx])) {
GUF_SWAP(GUF_OBJ_TYPE, arr[first_idx], arr[mid_idx]);
}
if (guf_before(&arr[last_idx], &arr[first_idx])) {
GUF_SWAP(GUF_OBJ_TYPE, arr[first_idx], arr[last_idx]);
}
if (guf_before(&arr[mid_idx], &arr[last_idx])) {
GUF_SWAP(GUF_OBJ_TYPE, arr[mid_idx], arr[last_idx]);
}
ptrdiff_t i = first_idx - 1; // i: end idx of the subarray with elements <= pivot
for (ptrdiff_t j = first_idx; j < last_idx; ++j) { // j: end idx of the subarray with elements > pivot
if (guf_before_or_equal(&arr[j], &arr[pivot_idx])) {
++i;
GUF_ASSERT(i >= 0 && i < last_idx);
GUF_SWAP(GUF_OBJ_TYPE, arr[i], arr[j]);
}
}
GUF_ASSERT(i + 1 >= 0 && i + 1 < last_idx);
GUF_SWAP(GUF_OBJ_TYPE, arr[i + 1], arr[last_idx]); // The pivot element is always <= itself (or >= for descending sorts).
pivot_idx = i + 1;
// 2.) Sort the two partitions [first_idx, pivot_idx) and (pivot_idx, last_idx] recursively.
/*
"To make sure at most O(log n) space is used, recur first into the smaller side of the partition,
then use a tail call to recur into the other, or update the parameters to no longer include the now sorted smaller side,
and iterate to sort the larger side.", cf. https://en.wikipedia.org/wiki/Quicksort#Optimizations (last-retrieved 2025-01-25)
(Solution to exercise "II.7.4c. Stack depth for quicksort". In "Introduction to Algorithms" (3rd ed, p. 188))
*/
if (pivot_idx <= mid_idx) { // a.) Left subarray is smaller or equal than the right subarray -> recur into the smaller left subarray first.
GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, first_idx, pivot_idx - 1, sort_opt, cmp);
first_idx = pivot_idx + 1;
} else { // b.) Right subarray is smaller than the left subarray -> recur into the smaller right subarray first.
GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, pivot_idx + 1, last_idx, sort_opt, cmp);
last_idx = pivot_idx - 1;
}
}
return arr;
}
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b))
{
GUF_ASSERT_RELEASE(arr);
GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING);
GUF_ASSERT_RELEASE(n >= 0);
if (n <= 1) {
return arr;
} else if (n <= 4) {
return GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(arr, n, sort_opt, cmp);
} else {
return GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, 0, n - 1, sort_opt, cmp);
}
}
#undef guf_before
#undef guf_before_or_equal
#endif
#undef GUF_OBJ_TYPE
#undef GUF_OBJ_TYPE_NAME

View File

@ -5,7 +5,11 @@
#include <stdbool.h>
#include "guf_common.h"
// #include "guf_obj.h"
#include "guf_alloc.h"
#define GUF_CNT_T char
#define GUF_CNT_NAME guf_dbuf_char
#include "guf_dbuf.h"
#define GUF_STR_ABORT_ON_ALLOC_FAILURE 1
@ -14,7 +18,8 @@
typedef enum guf_str_state {
GUF_STR_STATE_INIT = 0,
GUF_STR_STATE_SHORT = 1,
GUF_STR_STATE_ALLOC_ERR = 2
GUF_STR_STATE_READONLY = 2,
GUF_STR_STATE_ALLOC_ERR = 4
} guf_str_state;
typedef struct guf_str {
@ -23,6 +28,7 @@ typedef struct guf_str {
struct heap {
char *c_str;
size_t len, capacity; // len and capacity do not include the null-terminator.
guf_dbuf_char chars; // TODO
} heap;
struct stack { // Short-string optimisation.
#define GUF_STR_SSO_BUFSIZE (sizeof(struct heap) - sizeof(unsigned char))
@ -53,6 +59,8 @@ guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str);
guf_str *guf_str_init_empty_with_capacity(guf_str *str, size_t capacity);
// guf_str_new functions return GUF_DICT_UNINITIALISED or GUF_STR_UNINITIALISED_FAILED_ALLOC on failure (can be checked with guf_str_alloc_success)
guf_str guf_str_new(guf_str_view str_view);
guf_str guf_str_new_substr(guf_str_view str_view, ptrdiff_t pos, ptrdiff_t len);
guf_str guf_str_new_from_cstr(const char *c_str);
guf_str guf_str_new_empty_with_capacity(size_t capacity);
@ -61,7 +69,7 @@ void guf_str_free(guf_str *str);
// Modification:
guf_str *guf_str_append(guf_str *str, guf_str_view to_append);
guf_str *guf_str_append_cstr(guf_str *str, const char *cstr_to_append);
guf_str *guf_str_append_cstr(guf_str *str, const char *cstr_to_append); // Not necessary
guf_str *guf_str_substr(guf_str* str, size_t pos, size_t count);
guf_str *guf_str_reserve(guf_str *str, size_t bufsize);
@ -71,7 +79,7 @@ char guf_str_pop_back(guf_str *str);
char guf_str_pop_front(guf_str *str);
// Copying and viewing:
guf_str guf_str_substr_cpy(guf_str_view str, size_t pos, size_t count);
guf_str guf_str_substr_cpy(guf_str_view str, size_t pos, size_t count); // not necessary
guf_str_view guf_str_substr_view(guf_str_view str, size_t pos, size_t count);
// Indexing:
@ -98,39 +106,4 @@ int guf_str_view_cmp(const void *str_view_a, const void *str_view_b); // For qso
bool guf_str_char_is_ascii(char c);
bool guf_str_is_ascii(const guf_str *str);
// TODO:
// typedef struct guf_str_pool_elem {
// guf_str str;
// bool in_use;
// struct guf_str_pool_elem *next_free;
// } guf_str_pool_elem;
// typedef struct guf_str_pool {
// guf_str_pool_elem *elems;
// size_t capacity, num_in_use;
// size_t min_str_bufsize;
// guf_str_pool_elem *first_free;
// } guf_str_pool;
// void guf_str_pool_init(guf_str_pool *pool, size_t capacity, size_t str_initial_size)
// {
// if (capacity < 1) {
// capacity = 1;
// }
// pool->num_in_use = 0;
// pool->capacity = capacity;
// pool->elems = calloc(capacity, sizeof(guf_str_pool_elem));
// pool->min_str_bufsize = str_initial_size;
// GUF_ASSERT_RELEASE(pool->elems);
// pool->first_free = pool->elems + 0;
// for (size_t i = 0; i < pool->capacity; ++i) {
// pool->elems[i].in_use = false;
// pool->elems[i].str = guf_str_new_empty_with_capacity(pool->min_str_bufsize);
// pool->elems[i].next_free = i + 1 < pool->capacity ? pool->elems + i + 1 : NULL;
// }
// }
// find_free and find_free_with_cap
#endif

View File

@ -4,77 +4,107 @@
#define GUF_IMPLEMENTATION
#include "guf_common.h"
#undef GUF_IMPLEMENTATION
#include "guf_cstr.h"
#define GUF_CNT_NAME dbuf_int
#define GUF_CNT_T int
#define GUF_CNT_T_OPS guf_int_ops
#include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_char
#define GUF_CNT_T char
#define GUF_CNT_T_OPS guf_char_ops
#define GUF_CNT_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_float
#define GUF_CNT_T float
#define GUF_CNT_T_OPS guf_float_ops
#define GUF_CNT_T_IS_INTEGRAL_TYPE
#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_ops
#include "guf_dbuf.h"
#define GUF_OBJ_TYPE float
#include "guf_sort.h"
#define GUF_CNT_NAME dbuf_heap_cstr
#define GUF_CNT_T guf_heap_cstr
#define GUF_CNT_T_OPS guf_heap_cstr_ops
#define GUF_CNT_NAME dbuf_heap_cstr
#define GUF_CNT_T_COPY guf_heap_cstr_copy_construct
#define GUF_CNT_T_MOVE guf_heap_cstr_move_construct
#define GUF_CNT_T_FREE guf_heap_cstr_free
#define GUF_CNT_T_EQ guf_heap_cstr_eq
#include "guf_dbuf.h"
// typedef struct guf_test {
// const char *name, *expected_output;
// char *output;
// void (*test_fn)(struct guf_test *test);
// uint64_t runtime_ms;
// bool passed;
// } guf_test;
#define GUF_CNT_T guf_const_cstr
#define GUF_CNT_NAME dbuf_const_cstr
#define GUF_CNT_T_EQ guf_const_cstr_eq
#include "guf_dbuf.h"
int main(void)
{
bool success = true;
GUF_LIFETIME_BLOCK(dbuf_float floats = dbuf_float_new(), floats, dbuf_float_free, {
for (int i = 0; i < 10; ++i) {
GUF_LIFETIME_BLOCK(dbuf_float floats = dbuf_float_new(&guf_allocator_libc), floats, dbuf_float_free, {
for (int i = 0; i < 16; ++i) {
dbuf_float_push_val(&floats, i % 2 ? i * 2.718f : i * -2.718f);
}
GUF_FOREACH(&floats, dbuf_float, it) {
printf("float: %f\n", *it.cur);
}
// float *tmp = calloc(floats.size, sizeof(float));
// float *res = float_merge_sort(floats.data, tmp, floats.size, GUF_SORT_DESCENDING, NULL);
// free(tmp);
// GUF_ASSERT_RELEASE(res == floats.data);
dbuf_float_sort(&floats, GUF_SORT_ASCENDING);
printf("Sorted.\n");
GUF_FOREACH(&floats, dbuf_float, it) {
printf("float: %f\n", *it.cur);
// float_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL);
float_insertion_sort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL);
GUF_CNT_FOREACH(&floats, dbuf_float, it) {
printf("float: %f\n", *it.ptr);
}
})
dbuf_char str = dbuf_char_new();
dbuf_char_push_val(&str, 'H');
dbuf_char_push_val(&str, 'e');
dbuf_char_push_val(&str, 'j');
dbuf_char_push_val(&str, '\0');
printf("str.data = %s\n", str.data);
dbuf_char_free(&str);
dbuf_heap_cstr strings = dbuf_heap_cstr_new(&guf_allocator_libc);
dbuf_heap_cstr_push_val_cpy(&strings, "Foo 1");
dbuf_heap_cstr_push_val_cpy(&strings, "Bar 2");
char *move_me = strdup("Baz 3");
dbuf_heap_cstr_push(&strings, &move_me, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me == NULL);
dbuf_int integers = dbuf_int_new();
char *findme = "Baz 3";
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find(&strings, dbuf_heap_cstr_begin(&strings), dbuf_heap_cstr_end(&strings), &findme);
if (fnd_it.ptr != dbuf_heap_cstr_end(&strings).ptr) {
guf_heap_cstr_free(fnd_it.ptr);
*fnd_it.ptr = strdup("Found!");
}
GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) {
printf("%s\n", *it.ptr);
}
dbuf_heap_cstr_free(&strings);
dbuf_const_cstr const_strings = dbuf_const_cstr_new(&guf_allocator_libc);
dbuf_const_cstr_push_val(&const_strings, "Const 1");
dbuf_const_cstr_push_val(&const_strings, "Const 2");
const char *foo = "Const 3";
dbuf_const_cstr_push(&const_strings, &foo, GUF_CPY_VALUE);
dbuf_const_cstr_iter found_it = dbuf_const_cstr_find(&const_strings, dbuf_const_cstr_begin(&const_strings), dbuf_const_cstr_end(&const_strings), &foo);
if (found_it.ptr != dbuf_const_cstr_end(&const_strings).ptr) {
*found_it.ptr = "Found!";
}
GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) {
printf("%s\n", *it.ptr);
}
dbuf_const_cstr_free(&const_strings);
dbuf_int integers = dbuf_int_new(&guf_allocator_libc);
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);
dbuf_int_push_val(&integers, 820);
printf("%d\n", *dbuf_int_at(&integers, 4));
guf_err err;
dbuf_int_try_at(&integers, 16, &err);
if (err) {
printf("%s %s\n", guf_err_type_str[err], GUF_ERR_MSG_EMPTY());
}
int i = 0;
GUF_DBUF_FOREACH(integers, int, elem) {
@ -82,51 +112,29 @@ int main(void)
++i;
}
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);
GUF_CNT_FOREACH(&integers, dbuf_int, it) {
printf("it-elem: %d", *it.ptr);
if (dbuf_int_iter_next(&integers, it, 1).ptr != dbuf_int_end(&integers).ptr) {
printf(", it-next: %d", *dbuf_int_iter_next(&integers, it, 1).ptr);
}
if (it.next(&it, -1).cur != it.end) {
printf(", it-prev: %d", *it.next(&it, -1).cur);
if (dbuf_int_iter_next(&integers, it, -1).ptr != dbuf_int_end(&integers).ptr) {
printf(", it-prev: %d", *dbuf_int_iter_next(&integers, it, -1).ptr);
}
printf("\n");
}
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);
for (dbuf_int_iter it = dbuf_int_begin(&integers); it.ptr != dbuf_int_end(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) {
printf("every other: %d\n", *it.ptr);
}
dbuf_const_cstr strings = dbuf_const_cstr_new();
dbuf_const_cstr_push_val(&strings, "First");
printf("initial cap %td\n", strings.capacity);
dbuf_const_cstr_push_val(&strings, "Second");
guf_const_cstr foo = "Hello, World!";
dbuf_const_cstr_push(&strings, &foo, GUF_CPY_VALUE);
GUF_FOREACH(&strings, dbuf_const_cstr, it) {
printf("str: %s\n", *it.cur);
for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 1)) {
printf("reverse: %d\n", *it.ptr);
}
dbuf_heap_cstr mut_strings = dbuf_heap_cstr_new();
dbuf_heap_cstr_push_val_cpy(&mut_strings, "1");
dbuf_heap_cstr_push_val_cpy(&mut_strings, "2");
char *move_me_pls = calloc(128, sizeof(char));
strcpy(move_me_pls, "3");
dbuf_heap_cstr_push(&mut_strings, &move_me_pls, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me_pls == NULL);
dbuf_heap_cstr_sort(&mut_strings, GUF_SORT_DESCENDING);
GUF_FOREACH(&mut_strings, dbuf_heap_cstr, it) {
printf("str: %s\n", *it.cur);
for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) {
printf("every other reverse: %d\n", *it.ptr);
}
dbuf_heap_cstr_free(&mut_strings);
dbuf_const_cstr_free(&strings);
dbuf_int_free(&integers);
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}