Work on guf_dict
This commit is contained in:
parent
22ebd8aa05
commit
81fc19aa85
@ -16,7 +16,7 @@ if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
endif ()
|
||||
|
||||
add_executable(libguf_test ${SOURCES} src/guf_test.c)
|
||||
add_executable(libguf_test ${SOURCES} src/guf_test.c src/guf_test_dict_impl.c)
|
||||
target_include_directories(libguf_test PRIVATE src)
|
||||
|
||||
if (TARGET libguf_test)
|
||||
|
||||
@ -16,6 +16,8 @@ typedef enum guf_err {
|
||||
GUF_ERR_INVALID_ARG,
|
||||
GUF_ERR_RUNTIME,
|
||||
GUF_ERR_LOGIC,
|
||||
GUF_ERR_NOT_FOUND,
|
||||
GUF_ERR_ALREADY_EXISTS,
|
||||
GUF_ERR_ASSERT_FAIL,
|
||||
GUF_ERR_TYPES_NUM
|
||||
} guf_err;
|
||||
@ -81,6 +83,7 @@ static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
|
||||
#endif
|
||||
|
||||
#ifdef GUF_INIT
|
||||
#undef GUF_INIT
|
||||
static const char *guf_err_type_str[] = {
|
||||
[GUF_ERR_NONE] = "Not an error",
|
||||
[GUF_ERR_UNSPECIFIED] = "Error",
|
||||
@ -91,6 +94,8 @@ static const char *guf_err_type_str[] = {
|
||||
[GUF_ERR_DIV_BY_ZERO] = "Division by zero",
|
||||
[GUF_ERR_IDX_RANGE] = "Index out of range",
|
||||
[GUF_ERR_INVALID_ARG] = "Invalid argument",
|
||||
[GUF_ERR_NOT_FOUND] = "Not found error",
|
||||
[GUF_ERR_ALREADY_EXISTS] = "Already exists error",
|
||||
[GUF_ERR_RUNTIME] = "Runtime error",
|
||||
[GUF_ERR_LOGIC] = "Logic error",
|
||||
[GUF_ERR_ASSERT_FAIL] = "Assertion failed"
|
||||
@ -98,7 +103,6 @@ static const char *guf_err_type_str[] = {
|
||||
|
||||
extern void guf_panic(guf_err err, const char *msg)
|
||||
{
|
||||
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) {
|
||||
@ -118,12 +122,16 @@ extern void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler)
|
||||
|
||||
extern const char *guf_err_to_str(guf_err err)
|
||||
{
|
||||
GUF_ASSERT(GUF_STATIC_BUF_SIZE(guf_err_type_str) == GUF_ERR_TYPES_NUM);
|
||||
if (GUF_STATIC_BUF_SIZE(guf_err_type_str) != GUF_ERR_TYPES_NUM) {
|
||||
puts("Note: size of guf_err_type_str != GUF_ERR_TYPES_NUM");
|
||||
}
|
||||
|
||||
if (err < 0 || err >= GUF_ERR_TYPES_NUM) {
|
||||
return "Invalid error code";
|
||||
} else {
|
||||
GUF_ASSERT(err < GUF_STATIC_BUF_SIZE(guf_err_type_str));
|
||||
if (err > GUF_STATIC_BUF_SIZE(guf_err_type_str)) {
|
||||
return "Invalid error string";
|
||||
}
|
||||
return guf_err_type_str[err];
|
||||
}
|
||||
}
|
||||
@ -146,5 +154,4 @@ extern void guf_panic_handler_default(guf_err err, const char *msg)
|
||||
|
||||
guf_panic_handler_fn guf_global_panic_handler = guf_panic_handler_default;
|
||||
|
||||
#undef GUF_INIT
|
||||
#endif
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#ifndef GUF_CSTR_H
|
||||
#define GUF_CSTR_H
|
||||
#include <string.h>
|
||||
#include "guf_common.h"
|
||||
#include "guf_assert.h"
|
||||
#include "guf_hash.h"
|
||||
|
||||
typedef const char* guf_cstr_const;
|
||||
|
||||
@ -19,6 +21,13 @@ static inline bool guf_cstr_const_eq(const guf_cstr_const *a, const guf_cstr_con
|
||||
return 0 == strcmp(*a, *b);
|
||||
}
|
||||
|
||||
static inline guf_hash_size_t guf_cstr_const_hash(const guf_cstr_const *a)
|
||||
{
|
||||
GUF_ASSERT_RELEASE(a);
|
||||
GUF_ASSERT_RELEASE(*a);
|
||||
return guf_hash(*a, strlen(*a), GUF_HASH_INIT);
|
||||
}
|
||||
|
||||
typedef char* guf_cstr_heap;
|
||||
|
||||
static inline guf_cstr_heap *guf_cstr_heap_copy(guf_cstr_heap *dst, const guf_cstr_heap *src, void *ctx)
|
||||
|
||||
@ -29,6 +29,23 @@
|
||||
#define GUF_CNT_NAME GUF_CAT(dbuf_, GUF_T)
|
||||
#endif
|
||||
|
||||
#ifdef GUF_ONLY_TYPES
|
||||
#undef GUF_ONLY_TYPES
|
||||
typedef struct GUF_CNT_NAME {
|
||||
GUF_T *data;
|
||||
ptrdiff_t size, capacity;
|
||||
guf_allocator *allocator;
|
||||
#ifdef GUF_CNT_WITH_ELEM_CTX
|
||||
void *elem_ctx; // NULL by default; is passed as the ctx argument to GUF_T_COPY, GUF_T_MOVE and GUF_T_FREE
|
||||
#endif
|
||||
} GUF_CNT_NAME;
|
||||
|
||||
typedef struct GUF_CAT(GUF_CNT_NAME, _iter) {
|
||||
GUF_T *ptr;
|
||||
GUF_T *base; // Not NULL For reverse iterators (unless dbuf->size == 0, then ptr and base are NULL for both iterator types)
|
||||
} GUF_CAT(GUF_CNT_NAME, _iter);
|
||||
#else
|
||||
|
||||
// Used for the first growth if dbuf->capacity is zero.
|
||||
#ifndef GUF_DBUF_INITIAL_CAP
|
||||
#define GUF_DBUF_INITIAL_CAP 8
|
||||
@ -42,12 +59,14 @@
|
||||
#error "Integral types do not need COPY, MOVE, FREE or EQ functions"
|
||||
#endif
|
||||
|
||||
#ifdef GUF_IMPL_STATIC
|
||||
#if defined(GUF_STATIC) || defined(GUF_IMPL_STATIC)
|
||||
#define GUF_FN_KEYWORDS static
|
||||
#else
|
||||
#define GUF_FN_KEYWORDS
|
||||
#endif
|
||||
|
||||
#if !defined(GUF_IMPL)
|
||||
|
||||
typedef struct GUF_CNT_NAME {
|
||||
GUF_T *data;
|
||||
ptrdiff_t size, capacity;
|
||||
@ -65,12 +84,13 @@ typedef struct GUF_CNT_NAME {
|
||||
- rend(): base points to begin().ptr and ptr to NULL
|
||||
- reverse iterator for the first element of the container: base points to the second element, ptr points to the first element
|
||||
*/
|
||||
|
||||
typedef struct GUF_CAT(GUF_CNT_NAME, _iter) {
|
||||
GUF_T *ptr;
|
||||
GUF_T *base; // Not NULL For reverse iterators (unless dbuf->size == 0, then ptr and base are NULL for both iterator types)
|
||||
} GUF_CAT(GUF_CNT_NAME, _iter);
|
||||
|
||||
#endif
|
||||
|
||||
GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf);
|
||||
|
||||
GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err);
|
||||
@ -982,6 +1002,8 @@ GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_if)(GUF
|
||||
|
||||
#endif /* end #ifdef GUF_IMPL */
|
||||
|
||||
#endif /* end GUF_ONLY_TYPES */
|
||||
|
||||
#undef GUF_DBUF_INITIAL_CAP
|
||||
#undef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE
|
||||
|
||||
@ -998,3 +1020,4 @@ GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_if)(GUF
|
||||
#undef GUF_FN_KEYWORDS
|
||||
#undef GUF_IMPL
|
||||
#undef GUF_IMPL_STATIC
|
||||
#undef GUF_STATIC
|
||||
|
||||
524
src/guf_dict.h
524
src/guf_dict.h
@ -1,108 +1,460 @@
|
||||
#ifdef GUF_STATIC
|
||||
#define GUF_DICT_STATIC
|
||||
#undef GUF_STATIC
|
||||
#endif
|
||||
|
||||
#ifdef GUF_IMPL
|
||||
#define GUF_DICT_IMPL
|
||||
#undef GUF_IMPL
|
||||
#endif
|
||||
|
||||
#ifdef GUF_IMPL_STATIC
|
||||
#define GUF_DICT_IMPL_STATIC
|
||||
#undef GUF_IMPL_STATIC
|
||||
#endif
|
||||
|
||||
#if defined(GUF_DICT_STATIC) || defined(GUF_DICT_IMPL_STATIC)
|
||||
#define GUF_DICT_FN_KEYWORDS static
|
||||
#else
|
||||
#define GUF_DICT_FN_KEYWORDS
|
||||
#endif
|
||||
|
||||
#ifndef GUF_DICT_H
|
||||
#define GUF_DICT_H
|
||||
#include "guf_common.h"
|
||||
#include "guf_alloc.h"
|
||||
#include "guf_hash.h"
|
||||
|
||||
typedef enum guf_dict_probe_type {GUF_DICT_PROBE_LINEAR = 0, GUF_DICT_PROBE_QUADRATIC} guf_dict_probe_type;
|
||||
#define GUF_DICT_KV_IDX_NULL GUF_HASH_MAX
|
||||
#define GUF_DICT_KV_IDX_TOMBSTONE (GUF_HASH_MAX - 1)
|
||||
|
||||
// ~0.65 in fx10 fixed point (0.65 * 2^10)
|
||||
#define GUF_DICT_MAX_LOAD_FAC_FX10_DEFAULT 666ul
|
||||
#define GUF_DICT_PROBE_TYPE_DEFAULT GUF_DICT_PROBE_QUADRATIC
|
||||
typedef struct guf_dict_kv_meta {
|
||||
guf_hash_size_t kv_idx; // index into the key-value buffer. TODO: uint32_t?
|
||||
guf_hash_size_t key_hash;
|
||||
} guf_dict_kv_meta;
|
||||
|
||||
// #define GUF_HASH32_INIT 2166136261ul
|
||||
// #define GUF_HASH64_INIT 14695981039346656037ull
|
||||
// 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);
|
||||
// #define GUF_T guf_dict_kv_meta
|
||||
// #define GUF_CNT_NAME guf_dict_kv_meta_dbuf
|
||||
// #define GUF_ONLY_TYPES
|
||||
// #include "guf_dbuf.h"
|
||||
#endif
|
||||
|
||||
// #ifdef GUF_USE_32_BIT_HASH
|
||||
// #define GUF_HASH_INIT GUF_HASH32_INIT
|
||||
// static inline uint32_t guf_hash(const void *data, size_t num_bytes, uint32_t hash) {
|
||||
// return guf_hash32(data, num_bytes, hash);
|
||||
// }
|
||||
// #define GUF_DICT_HASH_MAX UINT32_MAX
|
||||
// #else
|
||||
// #define GUF_HASH_INIT GUF_HASH64_INIT
|
||||
// static inline uint64_t guf_hash(const void *data, size_t num_bytes, uint64_t hash) {
|
||||
// return guf_hash64(data, num_bytes, hash);
|
||||
// }
|
||||
// #define GUF_DICT_HASH_MAX UINT64_MAX
|
||||
// #endif
|
||||
#ifndef GUF_DICT_KEY_T
|
||||
#error "Undefined container template GUF_DICT_KEY_T"
|
||||
#endif
|
||||
|
||||
typedef struct guf_dict_kv_funcs {
|
||||
// Only used for keys:
|
||||
bool (*eq)(const void *key_a, const void *key_b); // Can be NULL for keys and vals. Can be left uninitialised for vals.
|
||||
guf_hash_size_t (*hash)(const void *key); // Can be NULL for keys and vals. Can be left uninitialised for vals.
|
||||
#ifndef GUF_DICT_KEY_HASH
|
||||
#error "Undefined container template GUF_DICT_KEY_HASH"
|
||||
#endif
|
||||
|
||||
// Used for keys and vals:
|
||||
void *(*cpy)(void *key_or_val_dst, const void *key_or_val_src); // Can be NULL for keys and vals. Never leave uninitialised.
|
||||
void *(*move)(void *dst, void *key_or_val_src); // Can be NULL for keys and vals. Never leave uninitialised.
|
||||
void (*free)(void *key_or_val); // Can be NULL for keys and vals. Never leave uninitialised.
|
||||
size_t type_size; // Must always be set to sizeof(key_or_val).
|
||||
#if !defined(GUF_DICT_KEY_T_EQ) && !defined(GUF_DICT_KEY_T_IS_INTEGRAL_TYPE)
|
||||
#error "Undefined container template GUF_DICT_KEY_T_EQ"
|
||||
#endif
|
||||
|
||||
} guf_dict_kv_funcs;
|
||||
#ifndef GUF_DICT_VAL_T
|
||||
#define GUF_DICT_IS_SET
|
||||
#endif
|
||||
|
||||
typedef guf_hash_size_t guf_dict_kv_id;
|
||||
#ifndef GUF_DICT_KEY_LOOKUP_T
|
||||
#define GUF_DICT_KEY_LOOKUP_T GUF_DICT_KEY_T
|
||||
#else
|
||||
// GUF_DICT_KEY_LOOKUP_T convert(const GUF_DICT_KEY_T *key)
|
||||
#ifndef GUF_DICT_KEY_TO_LOOKUP_KEY_CONVERT
|
||||
#error "GUF_DICT_KEY_TO_LOOKUP_KEY_CONVis must be defined since GUF_DICT_KEY_LOOKUP_T is defined"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct guf_dict_kv_status {
|
||||
guf_dict_kv_id kv_id;
|
||||
guf_hash_size_t k_hash;
|
||||
} guf_dict_kv_status;
|
||||
#ifndef GUF_DICT_NAME
|
||||
#define GUF_DICT_NAME GUF_CAT(dict_, GUF_CAT(GUF_DICT_KEY_T, GUF_CAT(_to_, GUF_DICT_VAL_T)))
|
||||
#endif
|
||||
#ifndef GUF_DICT_KV_NAME
|
||||
#define GUF_DICT_KV_NAME GUF_CAT(GUF_DICT_NAME, _kv)
|
||||
#endif
|
||||
|
||||
typedef struct guf_dict {
|
||||
guf_dict_kv_status *kv_status;
|
||||
void *keys, *vals;
|
||||
guf_dict_kv_funcs key_funcs, val_funcs;
|
||||
guf_dict_probe_type probe_t;
|
||||
uint32_t max_load_fac_fx10;
|
||||
size_t size, capacity_kv_status, capacity_key_val, num_tombstones, max_probelen;
|
||||
} guf_dict;
|
||||
#define GUF_DICT_KV_DBUF GUF_CAT(GUF_DICT_KV_NAME, _dbuf)
|
||||
|
||||
extern const guf_dict GUF_DICT_UNINITIALISED;
|
||||
// - GUF_T_COPY: cpy function with signature GUF_T *copy(GUF_T *dst, const GUF_T *src, void *ctx) (default: copy by value)
|
||||
// - GUF_T_MOVE: move function with signature GUF_T *move(GUF_T *dst, GUF_T *src, void *ctx) (default: undefined)
|
||||
// - GUF_T_FREE: free function with signature void free(GUF_T *a, void *ctx) (default: undefined)
|
||||
// - GUF_T_EQ: equality function with signature bool eq(const GUF_T *a, const GUF_T *a) (default: undefined, or equality by value if GUF_T_IS_INTEGRAL_TYPE is defined)
|
||||
|
||||
typedef enum guf_dict_insert_opt {
|
||||
GUF_DICT_CPY_KEY_VAL = 0,
|
||||
GUF_DICT_MOVE_KEY = 1,
|
||||
GUF_DICT_MOVE_VAL = 2,
|
||||
} guf_dict_insert_opt;
|
||||
|
||||
bool guf_dict_init(guf_dict *ht, size_t start_capacity, const guf_dict_kv_funcs *key_funcs, const guf_dict_kv_funcs *val_funcs);
|
||||
void guf_dict_free(guf_dict *ht);
|
||||
#ifndef GUF_DICT_IMPL
|
||||
|
||||
bool guf_dict_insert(guf_dict *ht, void *key, void *val); // bool copy_by_value
|
||||
bool guf_dict_remove(guf_dict *ht, const void *key);
|
||||
typedef struct GUF_DICT_KV_NAME {
|
||||
GUF_DICT_KEY_T key;
|
||||
#ifdef GUF_DICT_VAL_T
|
||||
GUF_DICT_VAL_T val;
|
||||
#endif
|
||||
} GUF_DICT_KV_NAME;
|
||||
|
||||
void *guf_dict_get_val(guf_dict *ht, const void *key);
|
||||
bool guf_dict_contains_key(const guf_dict *ht, const void *key);
|
||||
#define GUF_T GUF_DICT_KV_NAME
|
||||
#define GUF_CNT_NAME GUF_DICT_KV_DBUF
|
||||
#define GUF_ONLY_TYPES
|
||||
#include "guf_dbuf.h"
|
||||
|
||||
uint32_t guf_dict_load_factor_fx10(const guf_dict *ht);
|
||||
double guf_dict_load_factor_double(const guf_dict *ht);
|
||||
static inline uint32_t guf_dict_dbl_to_fx10_load_fac(double n)
|
||||
{
|
||||
n = GUF_CLAMP(n, 0, 1);
|
||||
const uint32_t fx10_scale = 1024; // 2^10
|
||||
return n * fx10_scale;
|
||||
}
|
||||
|
||||
typedef struct guf_dict_iter {
|
||||
guf_dict *ht;
|
||||
size_t idx;
|
||||
size_t elems_seen;
|
||||
const void *key;
|
||||
void *val;
|
||||
} guf_dict_iter;
|
||||
|
||||
guf_dict_iter guf_dict_iter_begin(guf_dict *ht);
|
||||
bool guf_dict_iter_is_end(guf_dict_iter *iter);
|
||||
void guf_dict_iter_advance(guf_dict_iter *iter);
|
||||
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_CSTR;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_GUF_STR;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_i32;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_i64;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_u32;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_u64;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_float;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_double;
|
||||
extern const guf_dict_kv_funcs GUF_DICT_FUNCS_NULL;
|
||||
typedef struct GUF_DICT_NAME {
|
||||
guf_dict_kv_meta *kv_meta_buf;
|
||||
GUF_DICT_KV_DBUF kv_dbuf;
|
||||
ptrdiff_t kv_meta_buf_cap, num_tombstones;
|
||||
ptrdiff_t max_probelen; // Debug
|
||||
} GUF_DICT_NAME;
|
||||
|
||||
#endif
|
||||
|
||||
GUF_DICT_FN_KEYWORDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _try_init)(GUF_DICT_NAME *ht, guf_allocator *alloc, guf_err *err);
|
||||
GUF_DICT_FN_KEYWORDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _init)(GUF_DICT_NAME *ht, guf_allocator *alloc);
|
||||
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _free)(GUF_DICT_NAME *ht, void *ctx);
|
||||
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T *key, GUF_DICT_VAL_T *val, guf_cpy_opt key_opt, guf_cpy_opt val_opt, guf_err *err);
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _insert)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T *key, GUF_DICT_VAL_T *val, guf_cpy_opt key_opt, guf_cpy_opt val_opt);
|
||||
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _try_erase)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key, guf_err *err);
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key);
|
||||
|
||||
#ifdef GUF_DICT_VAL_T
|
||||
GUF_DICT_FN_KEYWORDS GUF_DICT_VAL_T *GUF_CAT(GUF_DICT_NAME, _at)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_LOOKUP_T *key);
|
||||
#endif
|
||||
|
||||
GUF_DICT_FN_KEYWORDS bool GUF_CAT(GUF_DICT_NAME, _contains)(const GUF_DICT_NAME *ht, const GUF_DICT_KEY_LOOKUP_T *key);
|
||||
|
||||
GUF_DICT_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_DICT_NAME, _size)(const GUF_DICT_NAME *ht);
|
||||
|
||||
|
||||
// #define GUF_DICT_IMPL /* DEBUGGGGGGGGG */
|
||||
|
||||
#if defined(GUF_DICT_IMPL) || defined(GUF_DICT_IMPL_STATIC)
|
||||
|
||||
#include "guf_assert.h"
|
||||
|
||||
// #define GUF_T guf_dict_kv_meta
|
||||
// #define GUF_CNT_NAME guf_dict_kv_meta_dbuf
|
||||
// #define GUF_STATIC
|
||||
// #define GUF_IMPL
|
||||
// #include "guf_dbuf.h"
|
||||
|
||||
static inline void GUF_CAT(GUF_DICT_KV_NAME, _free)(GUF_DICT_KV_NAME *kv, void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
#ifdef GUF_DICT_KEY_T_FREE
|
||||
GUF_DICT_KEY_T_FREE(&kv->key, NULL);
|
||||
#endif
|
||||
#ifdef GUF_DICT_VAL_T_FREE
|
||||
GUF_DICT_VAL_T_FREE(&kv->val, NULL);
|
||||
#endif
|
||||
#if !defined(GUF_DICT_KEY_T_FREE) && !defined(GUF_DICT_VAL_T_FREE)
|
||||
(void)kv;
|
||||
#endif
|
||||
}
|
||||
#define GUF_T GUF_DICT_KV_NAME
|
||||
#define GUF_T_FREE GUF_CAT(GUF_DICT_KV_NAME, _free)
|
||||
#define GUF_CNT_NAME GUF_DICT_KV_DBUF
|
||||
#ifdef GUF_DICT_STATIC
|
||||
#define GUF_STATIC
|
||||
#endif
|
||||
#define GUF_IMPL
|
||||
#include "guf_dbuf.h"
|
||||
|
||||
static inline double GUF_CAT(GUF_DICT_NAME, _load_factor)(const GUF_DICT_NAME *ht)
|
||||
{
|
||||
if (ht->kv_meta_buf_cap == 0) {
|
||||
return 1;
|
||||
}
|
||||
ptrdiff_t occupied_count = ht->kv_dbuf.size + ht->num_tombstones;
|
||||
return (double)occupied_count / (double)ht->kv_meta_buf_cap;
|
||||
}
|
||||
|
||||
|
||||
GUF_DICT_FN_KEYWORDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _try_init)(GUF_DICT_NAME *ht, guf_allocator *alloc, guf_err *err)
|
||||
{
|
||||
if (!ht || !alloc) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in dict_try_init: ht or alloc NULL"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ht->kv_dbuf = (GUF_DICT_KV_DBUF){0};
|
||||
GUF_CAT(GUF_DICT_KV_DBUF, _try_init)(&ht->kv_dbuf, 0, alloc, err);
|
||||
if (err != GUF_ERR_NONE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ht->kv_meta_buf = NULL;
|
||||
ht->kv_meta_buf_cap = 0;
|
||||
|
||||
ht->num_tombstones = 0;
|
||||
ht->max_probelen = 0;
|
||||
return ht;
|
||||
}
|
||||
|
||||
GUF_DICT_FN_KEYWORDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _init)(GUF_DICT_NAME *ht, guf_allocator *alloc)
|
||||
{
|
||||
return GUF_CAT(GUF_DICT_NAME, _try_init)(ht, alloc, NULL);
|
||||
}
|
||||
|
||||
GUF_DICT_FN_KEYWORDS bool GUF_CAT(GUF_DICT_NAME, _valid)(const GUF_DICT_NAME *ht)
|
||||
{
|
||||
if (!ht) {
|
||||
return false;
|
||||
}
|
||||
bool kv_dbuf_valid = GUF_CAT(GUF_DICT_KV_DBUF, _valid)(&ht->kv_dbuf);
|
||||
bool kv_meta_buf_valid = (!ht->kv_meta_buf && !ht->kv_meta_buf_cap) || (ht->kv_meta_buf && ht->kv_meta_buf_cap);
|
||||
return kv_dbuf_valid && kv_meta_buf_valid;
|
||||
}
|
||||
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _free)(GUF_DICT_NAME *ht, void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
||||
guf_allocator *allocator = ht->kv_dbuf.allocator;
|
||||
|
||||
if (ht->kv_meta_buf) {
|
||||
allocator->free(ht->kv_meta_buf, ht->kv_meta_buf_cap * sizeof(guf_dict_kv_meta), allocator->ctx);
|
||||
ht->kv_meta_buf = NULL;
|
||||
ht->kv_meta_buf_cap = 0;
|
||||
}
|
||||
|
||||
GUF_CAT(GUF_DICT_KV_DBUF, _free)(&ht->kv_dbuf, NULL);
|
||||
|
||||
ht->num_tombstones = 0;
|
||||
ht->max_probelen = 0;
|
||||
}
|
||||
|
||||
|
||||
static inline size_t GUF_CAT(GUF_DICT_NAME, _probe_offset)(size_t probe_len)
|
||||
{
|
||||
GUF_ASSERT(probe_len > 0);
|
||||
#ifdef GUF_DICT_PROBE_LINEAR
|
||||
return 1;
|
||||
#else
|
||||
/*
|
||||
Guaranteed to visit each index once for capacities which are powers of two.
|
||||
cf. https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/ (last-retrieved 2024-07-29)
|
||||
*/
|
||||
return probe_len * (probe_len + 1) / 2; // 1, 3, 6, 10, 15, ... (starting from probe_len == 1)
|
||||
#endif
|
||||
}
|
||||
|
||||
static size_t GUF_CAT(GUF_DICT_NAME, _find_idx)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key, bool *key_exists, bool find_first_free)
|
||||
{
|
||||
if (ht->kv_meta_buf_cap == 0) {
|
||||
return SIZE_MAX;
|
||||
}
|
||||
const guf_hash_size_t hash = GUF_DICT_KEY_HASH(key);
|
||||
size_t idx = hash % ht->kv_meta_buf_cap;
|
||||
const size_t start_idx = idx;
|
||||
size_t probe_len = 1;
|
||||
size_t first_tombstone_idx = SIZE_MAX;
|
||||
do {
|
||||
// printf("idx : %zu %td\n", idx, ht->kv_meta_buf_cap);
|
||||
if (ht->kv_meta_buf[idx].kv_idx == GUF_DICT_KV_IDX_NULL) { // 1.) Empty.
|
||||
if (first_tombstone_idx != SIZE_MAX) {
|
||||
idx = first_tombstone_idx;
|
||||
}
|
||||
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
||||
GUF_ASSERT((ht->kv_meta_buf[idx].kv_idx == GUF_DICT_KV_IDX_NULL) || (ht->kv_meta_buf[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE));
|
||||
*key_exists = false;
|
||||
return idx;
|
||||
} else if (ht->kv_meta_buf[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) { // 2.) Tombstone.
|
||||
if (first_tombstone_idx == SIZE_MAX) {
|
||||
first_tombstone_idx = idx;
|
||||
}
|
||||
if (find_first_free) {
|
||||
goto end;
|
||||
} else {
|
||||
goto probe;
|
||||
}
|
||||
} else if (hash == ht->kv_meta_buf[idx].key_hash && GUF_DICT_KEY_T_EQ(key, &GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_dbuf, ht->kv_meta_buf[idx].kv_idx)->key)) { // 3.) Key already exists.
|
||||
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
||||
*key_exists = true;
|
||||
return idx;
|
||||
} else { // 4.) Have to probe due to hash-collision (idx is already occupied, but not by the key).
|
||||
probe:
|
||||
idx = (idx + GUF_CAT(GUF_DICT_NAME, _probe_offset)(probe_len)) % ht->kv_meta_buf_cap;
|
||||
++probe_len;
|
||||
GUF_ASSERT_RELEASE(probe_len < UINT32_MAX);
|
||||
}
|
||||
} while (idx != start_idx);
|
||||
|
||||
end:
|
||||
if (first_tombstone_idx != SIZE_MAX) { // Edge case: No empty slots, but found tombstone.
|
||||
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
||||
GUF_ASSERT(ht->kv_meta_buf[first_tombstone_idx].kv_idx == GUF_DICT_KV_IDX_NULL );
|
||||
*key_exists = false;
|
||||
return first_tombstone_idx;
|
||||
}
|
||||
*key_exists = false;
|
||||
return SIZE_MAX; // Failed to find an idx.
|
||||
}
|
||||
|
||||
GUF_DICT_FN_KEYWORDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T *key, GUF_DICT_VAL_T *val, guf_cpy_opt key_opt, guf_cpy_opt val_opt, guf_err *err)
|
||||
{
|
||||
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
||||
|
||||
const ptrdiff_t KV_META_START_CAP = 64;
|
||||
const ptrdiff_t KV_META_GROWTH_FAC = 2;
|
||||
|
||||
guf_allocator *allocator = ht->kv_dbuf.allocator;
|
||||
|
||||
if (ht->kv_meta_buf_cap == 0) {
|
||||
guf_dict_kv_meta *kv_meta_buf = allocator->alloc(KV_META_START_CAP * sizeof(guf_dict_kv_meta), allocator->ctx);
|
||||
if (kv_meta_buf == NULL) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: Initial allocation failed"));
|
||||
return;
|
||||
}
|
||||
ht->kv_meta_buf = kv_meta_buf;
|
||||
ht->kv_meta_buf_cap = KV_META_START_CAP;
|
||||
for (ptrdiff_t i = 0; i < ht->kv_meta_buf_cap; ++i) {
|
||||
kv_meta_buf[i].key_hash = 0;
|
||||
kv_meta_buf[i].kv_idx = GUF_DICT_KV_IDX_NULL;
|
||||
}
|
||||
} else if (GUF_CAT(GUF_DICT_NAME, _load_factor)(ht) > 0.6) {
|
||||
|
||||
const ptrdiff_t old_size = ht->kv_meta_buf_cap * sizeof(guf_dict_kv_meta);
|
||||
ptrdiff_t new_size = 0;
|
||||
if (!guf_size_calc_safe(old_size, KV_META_GROWTH_FAC, &new_size)) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: New capacity would overflow)"));
|
||||
return;
|
||||
}
|
||||
guf_dict_kv_meta *kv_meta_buf = allocator->alloc(new_size, allocator->ctx);
|
||||
if (kv_meta_buf == NULL) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: allocation failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
const ptrdiff_t new_kv_meta_cap = ht->kv_meta_buf_cap * KV_META_GROWTH_FAC;
|
||||
for (ptrdiff_t i = 0; i < new_kv_meta_cap; ++i) {
|
||||
kv_meta_buf[i].key_hash = 0;
|
||||
kv_meta_buf[i].kv_idx = GUF_DICT_KV_IDX_NULL;
|
||||
}
|
||||
|
||||
guf_dict_kv_meta *old_kv_meta = ht->kv_meta_buf;
|
||||
ptrdiff_t old_kv_meta_cap = ht->kv_meta_buf_cap;
|
||||
|
||||
ht->kv_meta_buf = kv_meta_buf;
|
||||
ht->kv_meta_buf_cap = new_kv_meta_cap;
|
||||
ht->num_tombstones = 0;
|
||||
|
||||
ptrdiff_t cnt = 0;
|
||||
for (ptrdiff_t i = 0; i < old_kv_meta_cap; ++i) { // Insert into new buffer.
|
||||
if (old_kv_meta[i].kv_idx != GUF_DICT_KV_IDX_NULL && old_kv_meta[i].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE) {
|
||||
bool key_exists = false;
|
||||
size_t new_idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists, false);
|
||||
GUF_ASSERT(!key_exists);
|
||||
GUF_ASSERT(new_idx < SIZE_MAX);
|
||||
ht->kv_meta_buf[new_idx] = old_kv_meta[i];
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
GUF_ASSERT(cnt == ht->kv_dbuf.size);
|
||||
|
||||
allocator->free(old_kv_meta, old_size, allocator->ctx);
|
||||
|
||||
GUF_ASSERT(GUF_CAT(GUF_DICT_NAME, _load_factor)(ht) < 0.6);
|
||||
}
|
||||
|
||||
GUF_ASSERT(ht->kv_meta_buf_cap > ht->kv_dbuf.size);
|
||||
|
||||
bool key_exists = false;
|
||||
size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists, false);
|
||||
if (key_exists) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALREADY_EXISTS, GUF_ERR_MSG("in function dict_try_insert: Key already exists"));
|
||||
return;
|
||||
}
|
||||
GUF_ASSERT(idx < (size_t)ht->kv_meta_buf_cap);
|
||||
|
||||
if (ht->kv_meta_buf[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) {
|
||||
ht->num_tombstones -= 1;
|
||||
GUF_ASSERT_RELEASE(ht->num_tombstones >= 0);
|
||||
}
|
||||
|
||||
ht->kv_meta_buf[idx].key_hash = GUF_DICT_KEY_HASH(key);
|
||||
ht->kv_meta_buf[idx].kv_idx = ht->kv_dbuf.size;
|
||||
|
||||
GUF_DICT_KEY_T key_cpy;
|
||||
GUF_DICT_KEY_T *key_cpy_res = NULL;
|
||||
if (key_opt == GUF_CPY_DEEP) {
|
||||
#ifdef GUF_DICT_KEY_T_COPY
|
||||
key_cpy_res = GUF_DICT_KEY_T_COPY(&key_cpy, key);
|
||||
#else
|
||||
GUF_ASSERT_RELEASE(false);
|
||||
#endif
|
||||
} else if (key_opt == GUF_CPY_MOVE) {
|
||||
#ifdef GUF_DICT_KEY_T_MOVE
|
||||
key_cpy_res = GUF_DICT_KEY_T_MOVE(&key_cpy, key);
|
||||
#else
|
||||
GUF_ASSERT_RELEASE(false);
|
||||
#endif
|
||||
} else {
|
||||
key_cpy = *key;
|
||||
key_cpy_res = &key_cpy;
|
||||
}
|
||||
if (!key_cpy_res) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: Failed to copy key"));
|
||||
return;
|
||||
}
|
||||
|
||||
GUF_DICT_VAL_T val_cpy;
|
||||
GUF_DICT_VAL_T *val_cpy_res = NULL;
|
||||
if (val_opt == GUF_CPY_DEEP) {
|
||||
#ifdef GUF_DICT_VAL_T_COPY
|
||||
val_cpy_res = GUF_DICT_KEY_T_COPY(&val_cpy, val);
|
||||
#else
|
||||
GUF_ASSERT_RELEASE(false);
|
||||
#endif
|
||||
} else if (val_opt == GUF_CPY_MOVE) {
|
||||
#ifdef GUF_DICT_VAL_T_MOVE
|
||||
val_cpy_res = GUF_DICT_KEY_T_MOVE(&val_cpy, val);
|
||||
#else
|
||||
GUF_ASSERT_RELEASE(false);
|
||||
#endif
|
||||
} else {
|
||||
val_cpy = *val;
|
||||
val_cpy_res = &val_cpy;
|
||||
}
|
||||
if (!val_cpy_res) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: Failed to copy value"));
|
||||
if (key_opt == GUF_CPY_DEEP || key_opt == GUF_CPY_MOVE) {
|
||||
#ifdef GUF_DICT_KEY_T_FREE
|
||||
GUF_DICT_KEY_T_FREE(key_cpy_res, NULL);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GUF_DICT_KV_NAME kv = {.key = key_cpy, .val = val_cpy};
|
||||
GUF_CAT(GUF_DICT_KV_DBUF, _try_push_val)(&ht->kv_dbuf, kv, err);
|
||||
}
|
||||
|
||||
#endif /* end GUF_IMPL/GUF_IMPL_STATIC */
|
||||
|
||||
#undef GUF_DICT_NAME
|
||||
#undef GUF_DICT_IS_SET
|
||||
#undef GUF_DICT_PROBE_LINEAR
|
||||
|
||||
#undef GUF_DICT_KEY_T
|
||||
#undef GUF_DICT_KEY_T_IS_INTEGRAL_TYPE
|
||||
#undef GUF_DICT_KEY_T_EQ
|
||||
#undef GUF_DICT_KEY_T_FREE
|
||||
#undef GUF_DICT_KEY_T_CMP
|
||||
#undef GUF_DICT_KEY_T_COPY
|
||||
#undef GUF_DICT_KEY_T_MOVE
|
||||
#undef GUF_DICT_KEY_HASH
|
||||
|
||||
#undef GUF_DICT_VAL_T
|
||||
#undef GUF_DICT_VAL_T_EQ
|
||||
#undef GUF_DICT_VAL_T_FREE
|
||||
#undef GUF_DICT_VAL_T_CMP
|
||||
#undef GUF_DICT_VAL_T_COPY
|
||||
#undef GUF_DICT_VAL_T_MOVE
|
||||
|
||||
#undef GUF_DICT_KEY_LOOKUP_T
|
||||
#undef GUF_DICT_KEY_TO_LOOKUP_KEY_CONVERT
|
||||
|
||||
#undef GUF_DICT_KV_NAME
|
||||
#undef GUF_DICT_KV_DBUF
|
||||
|
||||
#undef GUF_DICT_IMPL_STATIC
|
||||
#undef GUF_DICT_IMPL
|
||||
#undef GUF_DICT_STATIC
|
||||
#undef GUF_DICT_FN_KEYWORDS
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
cf. http://www.isthe.com/chongo/tech/comp/fnv/ (last retrieved: 2023-11-30)
|
||||
*/
|
||||
|
||||
#ifdef GUF_IMPL_STATIC
|
||||
#if defined(GUF_IMPL_STATIC) || defined(GUF_STATIC)
|
||||
#define GUF_FN_KEYWORDS static
|
||||
#else
|
||||
#define GUF_FN_KEYWORDS
|
||||
@ -39,6 +39,7 @@ GUF_FN_KEYWORDS uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint6
|
||||
#endif
|
||||
|
||||
#if defined(GUF_IMPL) || defined(GUF_IMPL_STATIC)
|
||||
|
||||
#include "guf_assert.h"
|
||||
|
||||
GUF_FN_KEYWORDS uint32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint32_t hash)
|
||||
@ -72,3 +73,4 @@ GUF_FN_KEYWORDS uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint6
|
||||
#endif /* endif GUF_IMPL/GUF_IMPL_STATIC */
|
||||
|
||||
#undef GUF_FN_KEYWORDS
|
||||
#undef GUF_STATIC
|
||||
|
||||
@ -5,6 +5,9 @@
|
||||
#define GUF_INIT
|
||||
#include "guf_assert.h"
|
||||
|
||||
#define GUF_IMPL
|
||||
#include "guf_hash.h"
|
||||
|
||||
// static inline bool guf_init(void)
|
||||
// {
|
||||
// static bool guf_is_init = false;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#ifndef GUF_RAND_H
|
||||
#define GUF_RAND_H
|
||||
|
||||
#ifdef GUF_IMPL_STATIC
|
||||
#if defined(GUF_IMPL_STATIC) || defined(GUF_STATIC)
|
||||
#define GUF_FN_KEYWORDS static
|
||||
#else
|
||||
#define GUF_FN_KEYWORDS
|
||||
@ -77,6 +77,7 @@ GUF_FN_KEYWORDS void guf_randstate_init(guf_randstate *state, uint64_t seed)
|
||||
for (size_t i = 0; i < GUF_STATIC_BUF_SIZE(state->s); ++i) {
|
||||
state->s[i] = (uint32_t)(guf_rand_splitmix64(&seed) >> 32);
|
||||
}
|
||||
|
||||
if (!state->s[0] && !state->s[1] && !state->s[2] && !state->s[3]) { // State must not be only zeroes:
|
||||
state->s[0] = 0x9e3779b9; // arbitrary constant != 0
|
||||
seed = 0x9e3779b97f4a7c15;
|
||||
@ -406,4 +407,6 @@ GUF_FN_KEYWORDS float guf_rand_normal_sample_one_f32(guf_randstate *state, float
|
||||
#undef GUF_IMPL_STATIC
|
||||
#endif /* endif GUF_IMPL/GUF_IMPL_STATIC */
|
||||
|
||||
#undef GUF_STATIC
|
||||
#undef GUF_FN_KEYWORDS
|
||||
#undef GUF_RAND_32_BIT
|
||||
|
||||
@ -15,7 +15,7 @@ typedef enum guf_sort_opt {
|
||||
#define GUF_FN_NAME_PREFIX GUF_CAT(GUF_T, _arr)
|
||||
#endif
|
||||
|
||||
#ifdef GUF_IMPL_STATIC
|
||||
#if defined(GUF_IMPL_STATIC) || defined(GUF_STATIC)
|
||||
#define GUF_FN_KEYWORDS static
|
||||
#else
|
||||
#define GUF_FN_KEYWORDS
|
||||
@ -182,6 +182,7 @@ GUF_FN_KEYWORDS bool GUF_CAT(GUF_FN_NAME_PREFIX, _is_sorted)(GUF_T *arr, ptrdiff
|
||||
#undef GUF_IMPL_STATIC
|
||||
#endif /* end #ifdef GUF_IMPL */
|
||||
|
||||
#undef GUF_STATIC
|
||||
#undef GUF_FN_KEYWORDS
|
||||
#undef GUF_T
|
||||
#undef GUF_FN_NAME_PREFIX
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "guf_init.h" /* Must be included once (sets up the global panic handler) */
|
||||
#include "guf_init.h" /* Must be included once */
|
||||
#include "guf_alloc_libc.h"
|
||||
#include "guf_cstr.h"
|
||||
|
||||
@ -43,12 +43,25 @@
|
||||
#define GUF_IMPL_STATIC
|
||||
#include "guf_dbuf.h"
|
||||
|
||||
#define GUF_RAND_32_BIT
|
||||
#define GUF_IMPL_STATIC
|
||||
#include "guf_rand.h"
|
||||
|
||||
#include "guf_test_dict_impl.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
dict_cstr_int ht;
|
||||
dict_cstr_int_try_init(&ht, &guf_allocator_libc, NULL);
|
||||
|
||||
dict_cstr_int_kv kv = {.key = "Hello", .val = 42};
|
||||
dict_cstr_int_try_insert(&ht, &kv.key, &kv.val, GUF_CPY_VALUE, GUF_CPY_VALUE, NULL);
|
||||
|
||||
kv = (dict_cstr_int_kv){.key = "World", .val = 64};
|
||||
dict_cstr_int_try_insert(&ht, &kv.key, &kv.val, GUF_CPY_VALUE, GUF_CPY_VALUE, NULL);
|
||||
|
||||
dict_cstr_int_free(&ht, NULL);
|
||||
|
||||
|
||||
printf("libguf test: compiled with C %ld\n", __STDC_VERSION__);
|
||||
guf_allocator test_allocator = guf_allocator_libc;
|
||||
guf_libc_alloc_ctx test_allocator_ctx = {.alloc_type_id = 0, .thread_id = 0, .zero_init = true};
|
||||
|
||||
9
src/guf_test_dict_impl.c
Normal file
9
src/guf_test_dict_impl.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include "guf_test_dict_impl.h"
|
||||
|
||||
#define GUF_DICT_KEY_T guf_cstr_const
|
||||
#define GUF_DICT_KEY_T_EQ guf_cstr_const_eq
|
||||
#define GUF_DICT_KEY_HASH guf_cstr_const_hash
|
||||
#define GUF_DICT_VAL_T int
|
||||
#define GUF_DICT_NAME dict_cstr_int
|
||||
#define GUF_IMPL
|
||||
#include "guf_dict.h"
|
||||
14
src/guf_test_dict_impl.h
Normal file
14
src/guf_test_dict_impl.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef GUF_DICT_IMPL_H
|
||||
#define GUF_DICT_IMPL_H
|
||||
|
||||
#include "guf_common.h"
|
||||
#include "guf_cstr.h"
|
||||
|
||||
#define GUF_DICT_KEY_T guf_cstr_const
|
||||
#define GUF_DICT_KEY_HASH guf_cstr_const_hash
|
||||
#define GUF_DICT_KEY_T_EQ guf_cstr_const_eq
|
||||
#define GUF_DICT_VAL_T int
|
||||
#define GUF_DICT_NAME dict_cstr_int
|
||||
#include "guf_dict.h"
|
||||
|
||||
#endif
|
||||
@ -1,11 +1,18 @@
|
||||
#ifndef GUF_UTILS_H
|
||||
#define GUF_UTILS_H
|
||||
#include "guf_assert.h"
|
||||
|
||||
static inline bool guf_system_is_big_endian(void)
|
||||
static inline bool guf_platform_is_big_endian(void)
|
||||
{
|
||||
unsigned i = 1;
|
||||
const char *bytes = (const char*)&i;
|
||||
return bytes[0] != 1;
|
||||
}
|
||||
|
||||
static inline int guf_platform_native_word_bits(void)
|
||||
{
|
||||
const int bits = sizeof(void*) * CHAR_BIT;
|
||||
return bits;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user