Improve tests
This commit is contained in:
parent
c1e125dfcb
commit
6abe12c4c1
@ -3,7 +3,6 @@ set(PROJECT_NAME libguf)
|
||||
project(${PROJECT_NAME})
|
||||
|
||||
# set(SOURCES src/guf_str.c)
|
||||
|
||||
# add_library(${PROJECT_NAME} STATIC ${SOURCES})
|
||||
# target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf)
|
||||
# target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
@ -19,10 +18,10 @@ if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
endif ()
|
||||
|
||||
add_executable(libguf_example src/test/guf_example.c src/test/guf_dict_impl.c)
|
||||
add_executable(libguf_example src/test/example.c src/test/guf_dict_impl.c)
|
||||
target_include_directories(libguf_example PRIVATE src src/test)
|
||||
|
||||
add_executable(libguf_test src/test/guf_test.cpp src/test/guf_dict_impl.c src/test/guf_dbuf_impl.c src/test/guf_rand_impl.c)
|
||||
add_executable(libguf_test src/test/test.cpp src/test/guf_dbuf_impl.c src/test/guf_dict_impl.c src/test/guf_rand_impl.c src/test/guf_sort_impl.c)
|
||||
target_include_directories(libguf_test PRIVATE src src/test)
|
||||
|
||||
if (NOT DEFINED MSVC)
|
||||
|
||||
@ -138,8 +138,8 @@ GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t
|
||||
|
||||
GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err);
|
||||
GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf);
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt, guf_err *err);
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt);
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_move)(GUF_CNT_NAME *dbuf, guf_err *err);
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_move)(GUF_CNT_NAME *dbuf);
|
||||
|
||||
GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err);
|
||||
GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx);
|
||||
@ -651,12 +651,12 @@ GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf)
|
||||
GUF_CAT(GUF_CNT_NAME, _try_pop)(dbuf, NULL);
|
||||
}
|
||||
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt, guf_err *err)
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_move)(GUF_CNT_NAME *dbuf, guf_err *err)
|
||||
{
|
||||
GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf));
|
||||
|
||||
if (dbuf->size == 0) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_pop_cpy: Cannot pop from empty dbuf"));
|
||||
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_pop_move: Cannot pop from empty dbuf"));
|
||||
GUF_T dummy;
|
||||
memset(&dummy, 0, sizeof(GUF_T));
|
||||
return dummy;
|
||||
@ -665,47 +665,27 @@ GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, gu
|
||||
GUF_T *popped = dbuf->data + (dbuf->size - 1);
|
||||
GUF_T popped_val;
|
||||
GUF_T *dst = &popped_val;
|
||||
|
||||
if (!GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(cpy_opt)) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function " GUF_STRINGIFY(GUF_CAT(GUF_CNT_NAME, _copy_opt_available)) ": cpy_opt unavailable"));
|
||||
memset(&popped_val, 0, sizeof(popped_val));
|
||||
return popped_val;
|
||||
} else if (cpy_opt == GUF_CPY_DEEP) {
|
||||
#ifdef GUF_T_COPY
|
||||
dst = GUF_T_COPY(dst, popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf));
|
||||
#else
|
||||
GUF_ASSERT_RELEASE(false);
|
||||
#endif
|
||||
} else if (cpy_opt == GUF_CPY_MOVE) {
|
||||
#ifdef GUF_T_MOVE
|
||||
#if defined(GUF_T_MOVE)
|
||||
dst = GUF_T_MOVE(dst, popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf));
|
||||
#else
|
||||
GUF_ASSERT_RELEASE(false);
|
||||
#endif
|
||||
} else {
|
||||
GUF_ASSERT_RELEASE(cpy_opt == GUF_CPY_VALUE);
|
||||
*dst = *popped;
|
||||
}
|
||||
memset(popped, 0, sizeof(GUF_T));
|
||||
#endif
|
||||
|
||||
if (!dst) {
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_pop_cpy: Failed on copy"));
|
||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_pop_move: Failed on move"));
|
||||
memset(&popped_val, 0, sizeof(GUF_T));
|
||||
return popped_val; // Return a dummy value in case the copy failed (as we have to return something).
|
||||
} else {
|
||||
#ifdef GUF_T_FREE
|
||||
if (cpy_opt == GUF_CPY_DEEP) {
|
||||
GUF_T_FREE(popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf));
|
||||
}
|
||||
#endif
|
||||
dbuf->size--;
|
||||
guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
||||
return popped_val;
|
||||
}
|
||||
}
|
||||
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt)
|
||||
GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_move)(GUF_CNT_NAME *dbuf)
|
||||
{
|
||||
return GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(dbuf, cpy_opt, NULL);
|
||||
return GUF_CAT(GUF_CNT_NAME, _try_pop_move)(dbuf, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
649
src/guf_dict.c
649
src/guf_dict.c
@ -1,649 +0,0 @@
|
||||
// #include <stdint.h>
|
||||
// #include <stdbool.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <string.h>
|
||||
// #include <stdio.h>
|
||||
|
||||
// #include "guf_common.h"
|
||||
// #include "guf_dict.h"
|
||||
|
||||
// /*
|
||||
// FNV-1a (32 bit) hash function.
|
||||
// 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)
|
||||
// */
|
||||
|
||||
// uint32_t guf_hash32(const void *data, size_t num_bytes, uint32_t hash)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(data);
|
||||
// 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 < num_bytes; ++i) {
|
||||
// hash ^= data_bytes[i];
|
||||
// hash *= FNV_32_PRIME;
|
||||
// }
|
||||
// return hash;
|
||||
// }
|
||||
|
||||
// uint64_t guf_hash64(const void *data, size_t num_bytes, uint64_t hash)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(data);
|
||||
// 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 < num_bytes; ++i) {
|
||||
// hash ^= data_bytes[i];
|
||||
// hash *= FNV_64_PRIME;
|
||||
// }
|
||||
|
||||
// return hash;
|
||||
// }
|
||||
|
||||
// static inline size_t find_next_power_of_two(size_t num)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(num > 0);
|
||||
// size_t pof2 = 1;
|
||||
// while (pof2 < num) {
|
||||
// GUF_ASSERT_RELEASE(pof2 * 2 > pof2);
|
||||
// pof2 *= 2;
|
||||
// }
|
||||
// return pof2;
|
||||
// }
|
||||
|
||||
// static const guf_dict_kv_id KV_ID_NULL = GUF_DICT_HASH_MAX;
|
||||
// static const guf_dict_kv_id KV_ID_TOMBSTONE = GUF_DICT_HASH_MAX - 1;
|
||||
// static const guf_dict_kv_id KV_ID_MAX = GUF_DICT_HASH_MAX - 2;
|
||||
|
||||
// const guf_dict GUF_DICT_UNINITIALISED = {
|
||||
// .capacity_kv_status = 0,
|
||||
// .size = 0,
|
||||
// .num_tombstones = 0,
|
||||
// .keys = NULL,
|
||||
// .vals = NULL,
|
||||
// .val_funcs = {.eq = NULL, .hash = NULL, .cpy = NULL, .move = NULL, .free = NULL, .type_size = 0},
|
||||
// .kv_status = NULL,
|
||||
// .probe_t = 0,
|
||||
// .max_load_fac_fx10 = 0,
|
||||
// .max_probelen = 0,
|
||||
// };
|
||||
|
||||
// static inline void *cpy_key(guf_dict *ht, void *dst, const void *src, bool default_cpy)
|
||||
// {
|
||||
// if (default_cpy || ht->key_funcs.cpy == NULL) { // Default copy.
|
||||
// return memcpy(dst, src, ht->key_funcs.type_size);
|
||||
// } else {
|
||||
// return ht->key_funcs.cpy(dst, src);
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline void *cpy_val(guf_dict *ht, void *dst, const void *src, bool default_cpy)
|
||||
// {
|
||||
// if (dst == NULL || src == NULL) {
|
||||
// GUF_ASSERT_RELEASE(ht->val_funcs.type_size == 0);
|
||||
// return NULL;
|
||||
// }
|
||||
// if (default_cpy || ht->val_funcs.cpy == NULL) { // Default copy.
|
||||
// return memcpy(dst, src, ht->val_funcs.type_size);
|
||||
// } else {
|
||||
// return ht->val_funcs.cpy(dst, src);
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline void *cpy_or_move_val(guf_dict *ht, void *dst, void *src, guf_dict_insert_opt opts)
|
||||
// {
|
||||
// if (dst == NULL || src == NULL) {
|
||||
// GUF_ASSERT_RELEASE(ht->val_funcs.type_size == 0);
|
||||
// return NULL;
|
||||
// }
|
||||
// if ((opts & GUF_DICT_MOVE_VAL)) {
|
||||
// GUF_ASSERT_RELEASE(ht->val_funcs.move);
|
||||
// return ht->val_funcs.move(dst, src);
|
||||
// } else { // Default copy.
|
||||
// return cpy_val(ht, dst, src, false);
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline void *cpy_or_move_key(guf_dict *ht, void *dst, void *src, guf_dict_insert_opt opts)
|
||||
// {
|
||||
// if ((opts & GUF_DICT_MOVE_KEY)) {
|
||||
// GUF_ASSERT_RELEASE(ht->key_funcs.move);
|
||||
// return ht->key_funcs.move(dst, src);
|
||||
// } else {
|
||||
// return cpy_key(ht, dst, src, false);
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline guf_hash_size_t key_hash(const guf_dict *ht, const void *key)
|
||||
// {
|
||||
// if (ht->key_funcs.hash) {
|
||||
// return ht->key_funcs.hash(key);
|
||||
// } else { // Default hash function.
|
||||
// return guf_hash(key, ht->key_funcs.type_size, GUF_HASH_INIT);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// static inline bool kv_stat_occupied(guf_dict_kv_status kv_stat) {
|
||||
// return kv_stat.kv_id != KV_ID_NULL && kv_stat.kv_id != KV_ID_TOMBSTONE;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// static void *key_get(guf_dict *ht, guf_dict_kv_id idx)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(idx <= KV_ID_MAX);
|
||||
// char *ptr = (char*)ht->keys;
|
||||
// return ptr + idx * ht->key_funcs.type_size;
|
||||
|
||||
// }
|
||||
|
||||
// static void *val_get(guf_dict *ht, guf_dict_kv_id idx)
|
||||
// {
|
||||
// if (ht->val_funcs.type_size == 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
// GUF_ASSERT_RELEASE(idx <= KV_ID_MAX);
|
||||
|
||||
// char *ptr = (char*)ht->vals;
|
||||
// return ptr + idx * ht->val_funcs.type_size ;
|
||||
// }
|
||||
|
||||
// static inline bool key_eq(guf_dict *ht, const void *key_a, const void *key_b)
|
||||
// {
|
||||
// if (ht->key_funcs.eq) {
|
||||
// return ht->key_funcs.eq(key_a, key_b);
|
||||
// } else { // Default equality function.
|
||||
// return 0 == memcmp(key_a, key_b, ht->key_funcs.type_size);
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline bool key_eq_at(guf_dict *ht, size_t idx, const void *key, guf_hash_size_t hash_of_key)
|
||||
// {
|
||||
// GUF_ASSERT(idx < ht->capacity_kv_status);
|
||||
// GUF_ASSERT(kv_stat_occupied(ht->kv_status[idx]));
|
||||
|
||||
// if (ht->kv_status[idx].k_hash == hash_of_key) { // Hashes match -> we check if the keys are actually equal.
|
||||
// return key_eq(ht, key_get(ht, ht->kv_status[idx].kv_id), key);
|
||||
// } else {
|
||||
// // Hashes don't match -> early exit (we save a memory load from ht->kv_elems).
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline size_t probe_offset(size_t probe_len, guf_dict_probe_type probe_t)
|
||||
// {
|
||||
// GUF_ASSERT(probe_len > 0);
|
||||
// switch (probe_t)
|
||||
// {
|
||||
// case GUF_DICT_PROBE_LINEAR:
|
||||
// default:
|
||||
// return 1;
|
||||
// case GUF_DICT_PROBE_QUADRATIC:
|
||||
// /*
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// static inline size_t mod_pow2(size_t a, size_t b) // a mod b (with b being a power of two.)
|
||||
// {
|
||||
// GUF_ASSERT(b > 0);
|
||||
// return a & (b - 1);
|
||||
// }
|
||||
|
||||
// static size_t find_idx(guf_dict *ht, const void *key, bool *key_exists, bool find_first_free)
|
||||
// {
|
||||
// const guf_hash_size_t hash = key_hash(ht, key);
|
||||
// size_t idx = mod_pow2(hash, ht->capacity_kv_status); // hash % ht->capacity
|
||||
// const size_t start_idx = idx;
|
||||
// size_t probe_len = 1;
|
||||
// size_t first_tombstone_idx = SIZE_MAX;
|
||||
// do {
|
||||
// if (ht->kv_status[idx].kv_id == KV_ID_NULL) { // 1.) Empty.
|
||||
// if (first_tombstone_idx != SIZE_MAX) {
|
||||
// idx = first_tombstone_idx;
|
||||
// }
|
||||
// ht->max_probelen = GUF_MAX(probe_len, ht->max_probelen);
|
||||
// GUF_ASSERT(!kv_stat_occupied(ht->kv_status[idx]));
|
||||
// *key_exists = false;
|
||||
// return idx;
|
||||
// } else if (ht->kv_status[idx].kv_id == KV_ID_TOMBSTONE) { // 2.) Tombstone.
|
||||
// if (first_tombstone_idx == SIZE_MAX) {
|
||||
// first_tombstone_idx = idx;
|
||||
// }
|
||||
// if (find_first_free) {
|
||||
// goto end;
|
||||
// } else {
|
||||
// goto probe;
|
||||
// }
|
||||
// } else if (key_eq_at(ht, idx, key, hash)) { // 3.) Key already exists.
|
||||
// ht->max_probelen = GUF_MAX(probe_len, ht->max_probelen);
|
||||
// GUF_ASSERT(kv_stat_occupied(ht->kv_status[idx]));
|
||||
// *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 = mod_pow2(idx + probe_offset(probe_len, ht->probe_t), ht->capacity_kv_status);
|
||||
// ++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(probe_len, ht->max_probelen);
|
||||
// GUF_ASSERT(!kv_stat_occupied(ht->kv_status[first_tombstone_idx]));
|
||||
// *key_exists = false;
|
||||
// return first_tombstone_idx;
|
||||
// }
|
||||
|
||||
// *key_exists = false;
|
||||
// return SIZE_MAX; // Failed to find an idx.
|
||||
// }
|
||||
|
||||
// static void insert_kv(guf_dict *ht, void *key, void *val, size_t idx, guf_dict_insert_opt opts, bool default_cpy_key, bool default_cpy_val)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(idx < ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(ht->kv_status[idx].kv_id == KV_ID_NULL || ht->kv_status[idx].kv_id == KV_ID_TOMBSTONE);
|
||||
// GUF_ASSERT_RELEASE(!kv_stat_occupied(ht->kv_status[idx]));
|
||||
|
||||
// if (!default_cpy_key) {
|
||||
// if (!cpy_or_move_key(ht, key_get(ht, idx), key, opts)) {
|
||||
// cpy_key(ht, key_get(ht, idx), key, true);
|
||||
// }
|
||||
// } else {
|
||||
// cpy_key(ht, key_get(ht, idx), key, true);
|
||||
// }
|
||||
// if (!default_cpy_val) {
|
||||
// if (!cpy_or_move_val(ht, val_get(ht, idx), val, opts)) {
|
||||
// cpy_val(ht, val_get(ht, idx), val, true);
|
||||
// }
|
||||
// } else {
|
||||
// cpy_val(ht, val_get(ht, idx), val, true);
|
||||
// }
|
||||
|
||||
// if (ht->kv_status[idx].kv_id == KV_ID_TOMBSTONE) {
|
||||
// GUF_ASSERT_RELEASE(ht->num_tombstones > 0);
|
||||
// --ht->num_tombstones;
|
||||
// }
|
||||
|
||||
// ht->kv_status[idx].k_hash = key_hash(ht, key_get(ht, idx));
|
||||
// ++ht->size;
|
||||
// }
|
||||
|
||||
// static void update_v(guf_dict *ht, void *val, size_t idx, guf_dict_insert_opt opts)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(idx < ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(kv_stat_occupied(ht->kv_status[idx]));
|
||||
|
||||
// if (ht->val_funcs.free) {
|
||||
// ht->val_funcs.free(val_get(ht, idx));
|
||||
// }
|
||||
// cpy_or_move_val(ht, val_get(ht, idx), val, opts);
|
||||
// }
|
||||
|
||||
// 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)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(ht);
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status == 0 && ht->size == 0 && ht->num_tombstones == 0 && ht->max_probelen == 0);
|
||||
// GUF_ASSERT_RELEASE(ht->keys == NULL && ht->vals == NULL);
|
||||
|
||||
// GUF_ASSERT_RELEASE(key_funcs && val_funcs);
|
||||
// ht->key_funcs = *key_funcs;
|
||||
// ht->val_funcs = *val_funcs;
|
||||
|
||||
// if (val_funcs->type_size == 0) {
|
||||
// // TODO: is a set!
|
||||
// }
|
||||
// if (start_capacity < 1) {
|
||||
// start_capacity = 1;
|
||||
// }
|
||||
|
||||
// ht->capacity_kv_status = find_next_power_of_two(start_capacity);
|
||||
// ht->size = ht->num_tombstones = 0;
|
||||
// ht->max_probelen = 0;
|
||||
|
||||
// GUF_ASSERT_RELEASE(ht->key_funcs.type_size > 0);
|
||||
// // GUF_ASSERT_RELEASE(ht->val_funcs.type_size > 0);
|
||||
|
||||
// ht->probe_t = GUF_DICT_PROBE_QUADRATIC;
|
||||
// ht->max_load_fac_fx10 = GUF_DICT_MAX_LOAD_FAC_FX10_DEFAULT;
|
||||
// GUF_ASSERT_RELEASE(ht->max_load_fac_fx10 != 0 && ht->max_load_fac_fx10 <= 1024);
|
||||
|
||||
// ht->keys = calloc(ht->capacity_kv_status, ht->val_funcs.type_size);
|
||||
// if (!ht->keys) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// ht->vals = NULL;
|
||||
// if (ht->val_funcs.type_size > 0) {
|
||||
// ht->vals = calloc(ht->capacity_kv_status, ht->val_funcs.type_size);
|
||||
// if (!ht->vals) {
|
||||
// free(ht->keys);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// ht->kv_status = calloc(ht->capacity_kv_status, sizeof(uint8_t));
|
||||
// if (!ht->kv_status) {
|
||||
// free(ht->keys);
|
||||
// free(ht->vals);
|
||||
// return false;
|
||||
// }
|
||||
// for (size_t i = 0; i < ht->capacity_kv_status; ++i) {
|
||||
// GUF_ASSERT(ht->kv_status[i].kv_id == KV_ID_NULL);
|
||||
// }
|
||||
|
||||
// return ht;
|
||||
// }
|
||||
|
||||
// bool guf_dict_insert(guf_dict *ht, void *key, void *val, guf_dict_insert_opt opts)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(ht);
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status > 0 && ht->size <= ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(ht->keys && ht->vals && ht->kv_status);
|
||||
|
||||
// if ((opts & GUF_DICT_MOVE_KEY) && ht->key_funcs.move == NULL) {
|
||||
// // Ignore -Wunused-value.
|
||||
// fprintf(stderr, "guf_dict_insert: key_funcs.move is NULL while GUF_DICT_MOVE_KEY is set\n");
|
||||
// GUF_ASSERT(false);
|
||||
// return false;
|
||||
// }
|
||||
// if ((opts & GUF_DICT_MOVE_VAL) && ht->val_funcs.move == NULL) {
|
||||
// // Ignore -Wunused-value.
|
||||
// fprintf(stderr, "guf_dict_insert: val_funcs.move is NULL while GUF_DICT_MOVE_VAL is set\n");
|
||||
// GUF_ASSERT(false);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if (guf_dict_load_factor_fx10(ht) > ht->max_load_fac_fx10 || ht->capacity_kv_status == ht->size) { // Handle growth:
|
||||
// const size_t new_cap = 2 * ht->capacity_kv_status; // TODO: Limit to MAX_CAPACITY
|
||||
// bool overflow = new_cap <= ht->capacity_kv_status;
|
||||
// GUF_ASSERT(!overflow);
|
||||
// if (overflow) {
|
||||
// return false;
|
||||
// }
|
||||
// void *new_keys = calloc(new_cap, ht->key_funcs.type_size);
|
||||
// if (!new_keys) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// void *new_vals = NULL;
|
||||
// if (ht->val_funcs.type_size > 0) {
|
||||
// new_vals = calloc(new_cap, ht->val_funcs.type_size);
|
||||
// if (!new_vals) {
|
||||
// free(new_keys);
|
||||
// return false;
|
||||
// }
|
||||
// } else {
|
||||
// GUF_ASSERT_RELEASE(ht->vals == NULL);
|
||||
// }
|
||||
|
||||
// guf_dict_kv_status *new_kv_status = calloc(new_cap, sizeof(guf_dict_kv_status)); // No realloc here!
|
||||
// if (!new_kv_status) {
|
||||
// free(new_keys);
|
||||
// free(new_vals);
|
||||
// return false;
|
||||
// }
|
||||
// for (size_t i = 0; i < new_cap; ++i) {
|
||||
// new_kv_status[i].kv_id = KV_ID_NULL;
|
||||
// new_kv_status[i].k_hash = 0;
|
||||
// }
|
||||
|
||||
// guf_dict ht_new = *ht;
|
||||
// ht_new.kv_status = new_kv_status;
|
||||
// ht_new.keys = new_keys;
|
||||
// ht_new.vals = new_vals;
|
||||
// ht_new.size = 0;
|
||||
// ht_new.capacity_kv_status = new_cap;
|
||||
// ht_new.max_probelen = 0;
|
||||
|
||||
// size_t new_size = 0;
|
||||
// for (size_t i = 0; i < ht->capacity_kv_status; ++i) {
|
||||
// if (kv_stat_occupied(ht->kv_status[i])) {
|
||||
// bool key_exists = false;
|
||||
// const size_t new_idx = find_idx(&ht_new, key_get(ht, i), &key_exists, true);
|
||||
// GUF_ASSERT_RELEASE(new_idx != SIZE_MAX);
|
||||
// GUF_ASSERT_RELEASE(!key_exists);
|
||||
// bool dumb_copy_key = ht->key_funcs.move == NULL;
|
||||
// bool dumb_copy_val = ht->val_funcs.move == NULL;
|
||||
// insert_kv(&ht_new, key_get(ht, i), val_get(ht, i), new_idx, GUF_DICT_MOVE_KEY | GUF_DICT_MOVE_VAL, dumb_copy_key, dumb_copy_val);
|
||||
// ++new_size;
|
||||
// }
|
||||
// }
|
||||
// GUF_ASSERT_RELEASE(new_size == ht->size);
|
||||
// free(ht->kv_status);
|
||||
// free(ht->keys);
|
||||
// free(ht->vals);
|
||||
// *ht = ht_new;
|
||||
// }
|
||||
|
||||
// bool key_exists = false;
|
||||
// const size_t idx = find_idx(ht, key, &key_exists, true);
|
||||
// GUF_ASSERT_RELEASE(idx != SIZE_MAX);
|
||||
// GUF_ASSERT_RELEASE(!kv_stat_occupied(ht->kv_status[idx]));
|
||||
// if (key_exists) {
|
||||
// update_v(ht, val, idx, opts);
|
||||
// } else {
|
||||
// insert_kv(ht, key, val, idx, opts, false, false);
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool guf_dict_contains_key(const guf_dict *ht, const void *key)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(ht);
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status > 0 && ht->size <= ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE (ht->keys && ht->vals && ht->kv_status);
|
||||
|
||||
// bool key_exists = false;
|
||||
// const size_t idx = find_idx((guf_dict*)ht, key, &key_exists, false); // TODO: const cast
|
||||
// GUF_ASSERT_RELEASE(!(key_exists && idx == SIZE_MAX));
|
||||
// return key_exists;
|
||||
// }
|
||||
|
||||
// void *guf_dict_get_val(guf_dict *ht, const void *key)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(ht);
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status > 0 && ht->size <= ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(ht->keys && ht->vals && ht->kv_status);
|
||||
// if (ht->capacity_kv_status == 0 || ht->size == 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
// bool key_exists = false;
|
||||
// const size_t idx = find_idx(ht, key, &key_exists, false);
|
||||
// GUF_ASSERT_RELEASE(idx != SIZE_MAX);
|
||||
|
||||
// if (!key_exists) {
|
||||
// return NULL;
|
||||
// } else {
|
||||
// GUF_ASSERT(kv_stat_occupied(ht->kv_status[idx]));
|
||||
// return val_get(ht, idx);
|
||||
// }
|
||||
// }
|
||||
|
||||
// bool guf_dict_remove(guf_dict *ht, const void *key)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(ht);
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status > 0 && ht->size <= ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(ht->keys && ht->vals && ht->kv_status);
|
||||
|
||||
// if (ht->size == 0) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// bool key_exists = false;
|
||||
// const size_t idx = find_idx(ht, key, &key_exists, false);
|
||||
// if (!key_exists) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if (ht->key_funcs.free) {
|
||||
// ht->key_funcs.free(key_get(ht, idx));
|
||||
// }
|
||||
// if (ht->val_funcs.free) {
|
||||
// ht->val_funcs.free(val_get(ht, idx));
|
||||
// }
|
||||
// ht->kv_status[idx].kv_id = KV_ID_TOMBSTONE;
|
||||
// --ht->size;
|
||||
// ++ht->num_tombstones;
|
||||
// GUF_ASSERT(ht->size + ht->num_tombstones <= ht->capacity_kv_status);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// uint32_t guf_dict_load_factor_fx10(const guf_dict *ht)
|
||||
// {
|
||||
// const uint64_t fx10_scale = 1024; // 2^10 (represents 1 in fx10 fixed point format).
|
||||
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status >= ht->size);
|
||||
|
||||
// if (ht->capacity_kv_status == 0) {
|
||||
// return 0;
|
||||
// }
|
||||
// size_t size = ht->size + ht->num_tombstones;
|
||||
// // <=> size * fx_scale * fx_scale) / (ht->capacity * fx_scale)
|
||||
// GUF_ASSERT(size <= ht->capacity_kv_status);
|
||||
// uint64_t load_fac = (size * fx10_scale) / (uint64_t)(ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(load_fac <= fx10_scale);
|
||||
// return (uint32_t)load_fac;
|
||||
// }
|
||||
|
||||
// double guf_dict_load_factor_double(const guf_dict *ht)
|
||||
// {
|
||||
// GUF_ASSERT_RELEASE(ht->capacity_kv_status >= ht->size);
|
||||
|
||||
// if (ht->capacity_kv_status == 0 || ht->capacity_kv_status == 0) {
|
||||
// return 0.f;
|
||||
// }
|
||||
|
||||
// size_t size = ht->size + ht->num_tombstones;
|
||||
// GUF_ASSERT(size <= ht->capacity_kv_status);
|
||||
|
||||
// return size / (double)(ht->capacity_kv_status);
|
||||
// }
|
||||
|
||||
// void guf_dict_free(guf_dict *ht)
|
||||
// {
|
||||
// if (!ht) {
|
||||
// return;
|
||||
// }
|
||||
// if (!ht->kv_status && !ht->keys && !ht->vals && ht->size == 0) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// for (size_t i = 0; i < ht->size; ++i) { // TODO: ht->size, not ht->capacity ?
|
||||
// if (ht->keys && ht->key_funcs.free) {
|
||||
// ht->key_funcs.free(key_get(ht, i));
|
||||
// }
|
||||
// if (ht->vals && ht->val_funcs.free) {
|
||||
// ht->val_funcs.free(val_get(ht, i));
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// free(ht->keys);
|
||||
// free(ht->vals);
|
||||
// free(ht->kv_status);
|
||||
// ht->keys = NULL;
|
||||
// ht->vals = NULL;
|
||||
// ht->kv_status = NULL;
|
||||
// ht->capacity_kv_status = ht->size = ht->num_tombstones = ht->max_probelen = 0;
|
||||
// *ht = GUF_DICT_UNINITIALISED;
|
||||
// }
|
||||
|
||||
// guf_dict_iter guf_dict_iter_begin(guf_dict *ht)
|
||||
// {
|
||||
// guf_dict_iter iter = {.elems_seen = ht->size + 1, .idx = 0, .ht = ht, .key = NULL, .val = NULL};
|
||||
|
||||
// if (ht->size == 0) {
|
||||
// return iter; // end iter
|
||||
// }
|
||||
|
||||
// for (size_t idx = 0; idx < ht->capacity_kv_status; ++idx) {
|
||||
// if (kv_stat_occupied(ht->kv_status[idx])) {
|
||||
// iter.idx = idx;
|
||||
// iter.elems_seen = 1;
|
||||
// iter.key = key_get(iter.ht, iter.idx);
|
||||
// iter.val = val_get(iter.ht, iter.idx);
|
||||
// GUF_ASSERT_RELEASE(iter.key != NULL);
|
||||
// return iter;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return iter; // end iter
|
||||
// }
|
||||
|
||||
// bool guf_dict_iter_is_end(guf_dict_iter *iter)
|
||||
// {
|
||||
// return iter->elems_seen == iter->ht->size + 1;
|
||||
// }
|
||||
|
||||
// void guf_dict_iter_advance(guf_dict_iter *iter)
|
||||
// {
|
||||
// if (guf_dict_iter_is_end(iter)) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (iter->elems_seen == iter->ht->size) {
|
||||
// ++iter->elems_seen;
|
||||
// iter->key = NULL;
|
||||
// iter->val = NULL;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// GUF_ASSERT_RELEASE(iter->elems_seen < iter->ht->size);
|
||||
// GUF_ASSERT_RELEASE(iter->idx < iter->ht->capacity_kv_status);
|
||||
// GUF_ASSERT_RELEASE(iter->key);
|
||||
|
||||
// for (size_t idx = iter->idx + 1; idx < iter->ht->capacity_kv_status; ++idx) {
|
||||
// if (kv_stat_occupied(iter->ht->kv_status[idx])) {
|
||||
// iter->idx = idx;
|
||||
// iter->key = key_get(iter->ht, iter->idx);
|
||||
// iter->val = val_get(iter->ht, iter->idx);
|
||||
// ++iter->elems_seen;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// GUF_ASSERT_RELEASE(false);
|
||||
// iter->elems_seen = iter->ht->size + 1;
|
||||
// }
|
||||
|
||||
|
||||
// /*
|
||||
// Removal of keys without tombstones (only would work for linear probing I think).
|
||||
|
||||
// cf. https://stackoverflow.com/questions/9127207/hash-table-why-deletion-is-difficult-in-open-addressing-scheme/24886657#24886657 (last-retrieved 2024-07-26)
|
||||
// The following del function from https://github.com/attractivechaos/klib/blob/6f73c80c6409d6f91cdf66ec1a002177274da2e7/cpp/khashl.hpp#L142-L150 (last-retrieved 2024-07-26)
|
||||
|
||||
// int del(khint_t i) {
|
||||
// khint_t j = i, k, mask, nb = n_buckets();
|
||||
// if (keys == 0 || i >= nb) return 0;
|
||||
// mask = nb - khint_t(1);
|
||||
// while (1) {
|
||||
// j = (j + khint_t(1)) & mask;
|
||||
// if (j == i || !__kh_used(used, j)) break; //j==i only when the table is completely full
|
||||
// k = __kh_h2b(Hash()(keys[j]), bits);
|
||||
// if (k <= i || k > j)
|
||||
// keys[i] = keys[j], i = j;
|
||||
// }
|
||||
// __kh_set_unused(used, i);
|
||||
// --count;
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// cf. https://en.wikipedia.org/w/index.php?title=Hash_table&oldid=95275577 (last-retrieved 2024-07-26)
|
||||
// Note:
|
||||
// - For all records in a cluster, there must be no vacant slots between their natural hash position
|
||||
// and their current position (else lookups will terminate before finding the record).
|
||||
|
||||
// - i is a vacant slot that might be invalidating this property for subsequent records in the cluster.
|
||||
// - j is such a subsequent record.
|
||||
// - k is the raw hash where the record at j would naturally land in the hash table if there were no collisions.
|
||||
|
||||
// - This test is asking if the record at j is invalidly positioned with respect
|
||||
// to the required properties of a cluster now that i is vacant.
|
||||
// */
|
||||
17
src/test/guf_sort_impl.c
Normal file
17
src/test/guf_sort_impl.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include "guf_sort_impl.h"
|
||||
|
||||
#define GUF_T float
|
||||
#define GUF_IMPL
|
||||
#include "guf_sort.h"
|
||||
|
||||
#define GUF_T int32_t
|
||||
#define GUF_IMPL
|
||||
#include "guf_sort.h"
|
||||
|
||||
#define GUF_T int8_t
|
||||
#define GUF_IMPL
|
||||
#include "guf_sort.h"
|
||||
|
||||
#define GUF_T guf_cstr_heap
|
||||
#define GUF_IMPL
|
||||
#include "guf_sort.h"
|
||||
17
src/test/guf_sort_impl.h
Normal file
17
src/test/guf_sort_impl.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef GUF_SORT_IMPL_H
|
||||
#define GUF_SORT_IMPL_H
|
||||
#include "guf_cstr.h"
|
||||
|
||||
#define GUF_T float
|
||||
#include "guf_sort.h"
|
||||
|
||||
#define GUF_T int32_t
|
||||
#include "guf_sort.h"
|
||||
|
||||
#define GUF_T int8_t
|
||||
#include "guf_sort.h"
|
||||
|
||||
#define GUF_T guf_cstr_heap
|
||||
#include "guf_sort.h"
|
||||
|
||||
#endif
|
||||
@ -1,321 +0,0 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "guf_init.h"
|
||||
#include "guf_alloc_libc.h"
|
||||
#include "guf_dbuf_impl.h"
|
||||
#include "guf_dict_impl.h"
|
||||
#include "guf_rand.h"
|
||||
}
|
||||
|
||||
struct Test
|
||||
{
|
||||
const std::string name {};
|
||||
std::chrono::duration<float, std::milli> runtime_ms {0};
|
||||
bool passed {false}, done {false};
|
||||
size_t num_failed_checks {0}, num_passed_checks {0};
|
||||
|
||||
Test(std::string& name) : name{name} {}
|
||||
virtual ~Test() = default;
|
||||
|
||||
bool operator==(const Test &other) const {return name == other.name;}
|
||||
|
||||
size_t total_checks() const {return num_passed_checks + num_failed_checks;}
|
||||
|
||||
virtual bool run() = 0;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream &os, const Test& tst)
|
||||
{
|
||||
std::ios_base::fmtflags os_flags = os.flags();
|
||||
std::streamsize os_precision = os.precision();
|
||||
|
||||
os << tst.name << ": " << (tst.passed ? "PASS" : "FAIL");
|
||||
os << std::fixed << std::setprecision(2);
|
||||
os << " (" << tst.num_passed_checks << "/" << tst.total_checks() << ") in " << tst.runtime_ms.count() << " ms";
|
||||
|
||||
os.precision(os_precision);
|
||||
os.flags(os_flags);
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<std::unique_ptr<Test>> {
|
||||
std::size_t operator()(const std::unique_ptr<Test>& test)
|
||||
{
|
||||
if (!test.get()) {
|
||||
return 0;
|
||||
}
|
||||
return std::hash<std::string>()(test.get()->name);
|
||||
}
|
||||
};
|
||||
|
||||
#define GUF_TEST_CHECK(COND, MSG, TEST_OBJ_PTR) do { \
|
||||
if (!(COND)) { \
|
||||
std::cerr << this->name << ": FAILED CHECK " << MSG << " (line " << __LINE__ << " file " << __FILE__ << ")\n"; \
|
||||
TEST_OBJ_PTR->num_failed_checks++; \
|
||||
} else { \
|
||||
TEST_OBJ_PTR->num_passed_checks++; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
|
||||
struct DbufIntTest : public Test
|
||||
{
|
||||
DbufIntTest(std::string name) : Test(name) {};
|
||||
|
||||
private:
|
||||
|
||||
std::vector<int> dbuf_to_vec(dbuf_int *dbuf)
|
||||
{
|
||||
std::vector<int> vec;
|
||||
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
|
||||
vec.push_back(*it.ptr);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void test_push(dbuf_int *dbuf, int n)
|
||||
{
|
||||
std::vector<int> vec = dbuf_to_vec(dbuf);
|
||||
|
||||
GUF_TEST_CHECK(std::ssize(vec) == dbuf->size, "test_push(): initial size", this);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dbuf_int_push_val(dbuf, i);
|
||||
vec.push_back(i);
|
||||
GUF_TEST_CHECK(*dbuf_int_back(dbuf) == vec.back(), "test_push(): equal while pushing", this);
|
||||
}
|
||||
|
||||
ptrdiff_t i = 0;
|
||||
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
|
||||
GUF_TEST_CHECK(*it.ptr == vec.at(i++), "test_push(): equal forward iteration", this);
|
||||
}
|
||||
GUF_TEST_CHECK(i == dbuf->size, "test_push(): size after iterating", this);
|
||||
|
||||
i = dbuf->size - 1;
|
||||
GUF_CNT_FOREACH_REVERSE(dbuf, dbuf_int, rit) {
|
||||
GUF_TEST_CHECK(*rit.ptr == vec.at(i--), "test_push(): equal reverse iteration", this);
|
||||
}
|
||||
GUF_TEST_CHECK(i == -1, "test_push(): size after reverse iteration", this);
|
||||
|
||||
}
|
||||
|
||||
void test_insert_remove(int n)
|
||||
{
|
||||
dbuf_int dbuf = {};
|
||||
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
|
||||
std::vector<int> vec = dbuf_to_vec(&dbuf);
|
||||
|
||||
guf_err err = GUF_ERR_NONE;
|
||||
dbuf_int_try_erase(&dbuf, 0, &err);
|
||||
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty test 1", this);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_erase(&dbuf, 12, &err);
|
||||
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty test 2", this);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_front(&dbuf, &err);
|
||||
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty test 3", this);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_back(&dbuf, &err);
|
||||
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty back 4", this);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_at(&dbuf, 0, &err);
|
||||
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty back 5", this);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dbuf_int_insert_val(&dbuf, i, i);
|
||||
dbuf_int_insert_val(&dbuf, i * 2, 0);
|
||||
dbuf_int_insert_val(&dbuf, i * 4, dbuf.size);
|
||||
|
||||
vec.insert(vec.begin() + i, i);
|
||||
vec.insert(vec.begin(), i * 2);
|
||||
vec.insert(vec.end(), i * 4);
|
||||
}
|
||||
GUF_TEST_CHECK(std::ssize(vec) == dbuf.size, "test_remove(): size after insert", this);
|
||||
|
||||
// Iterate
|
||||
dbuf_int_iter it_dbuf = dbuf_int_begin(&dbuf);
|
||||
std::vector<int>::const_iterator it_vec = vec.begin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
|
||||
GUF_TEST_CHECK(*it_dbuf.ptr == *it_vec, "test_remove(): iterate equal", this);
|
||||
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 1);
|
||||
std::advance(it_vec, 1);
|
||||
}
|
||||
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end(), "test_remove(): iterate equal 2", this);
|
||||
|
||||
// Step iterate.
|
||||
it_dbuf = dbuf_int_begin(&dbuf);
|
||||
it_vec = vec.begin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
|
||||
GUF_TEST_CHECK(*it_dbuf.ptr == *it_vec, "test_remove(): iterate step equal", this);
|
||||
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 7);
|
||||
|
||||
if (dbuf_int_iter_is_end(&dbuf, it_dbuf)) {
|
||||
it_vec = vec.end();
|
||||
} else {
|
||||
std::advance(it_vec, 7);
|
||||
}
|
||||
}
|
||||
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end(), "test_remove(): iterat step equal 2", this);
|
||||
|
||||
// Reverse iterate.
|
||||
dbuf_int_iter rit_dbuf = dbuf_int_rbegin(&dbuf);
|
||||
std::vector<int>::const_reverse_iterator rit_vec = vec.crbegin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
|
||||
GUF_TEST_CHECK(*rit_dbuf.ptr == *rit_vec, "test_remove(): reverse iterate equal", this);
|
||||
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 1);
|
||||
std::advance(rit_vec, 1);
|
||||
}
|
||||
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend(), "test_remove(): reverse iterate equal 2", this);
|
||||
|
||||
// Reverse iterate step.
|
||||
rit_dbuf = dbuf_int_rbegin(&dbuf);
|
||||
rit_vec = vec.crbegin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
|
||||
GUF_TEST_CHECK(*rit_dbuf.ptr == *rit_vec, "test_remove(): reverse iterate step equal", this);
|
||||
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 4);
|
||||
if (dbuf_int_iter_is_end(&dbuf, rit_dbuf)) {
|
||||
rit_vec = vec.rend();
|
||||
} else {
|
||||
std::advance(rit_vec, 4);
|
||||
}
|
||||
}
|
||||
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend(), "test_remove(): reverse iterate step equal 2", this);
|
||||
|
||||
|
||||
GUF_TEST_CHECK(dbuf.size == std::ssize(vec), "test_remove(): size equal", this);
|
||||
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
|
||||
dbuf_int_erase(&dbuf, i);
|
||||
dbuf_int_erase(&dbuf, 0);
|
||||
dbuf_int_pop(&dbuf);
|
||||
|
||||
vec.erase(vec.begin() + i);
|
||||
vec.erase(vec.begin() + 0);
|
||||
vec.pop_back();
|
||||
}
|
||||
|
||||
GUF_TEST_CHECK(dbuf.size == std::ssize(vec), "test_remove(): size equal after remove", this);
|
||||
|
||||
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
|
||||
GUF_TEST_CHECK(*dbuf_int_at(&dbuf, i) == vec.at(i), "test_remove(): equal after remove", this);
|
||||
}
|
||||
|
||||
const ptrdiff_t size = dbuf.size;
|
||||
for (ptrdiff_t i = 0; i < size; ++i) {
|
||||
int a = dbuf_int_pop_cpy(&dbuf, GUF_CPY_VALUE);
|
||||
int b = vec.back();
|
||||
GUF_TEST_CHECK(a == b, "test_remove(): equal pop", this);
|
||||
vec.pop_back();
|
||||
}
|
||||
GUF_TEST_CHECK(dbuf.size == 0 && vec.size() == 0, "test_remove(): equal after removing all", this);
|
||||
|
||||
dbuf_int_free(&dbuf, NULL);
|
||||
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && !dbuf.data, "test_remove(): state after free", this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool run() override
|
||||
{
|
||||
if (done) {
|
||||
return passed;
|
||||
}
|
||||
auto t0 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
dbuf_int dbuf {};
|
||||
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
|
||||
|
||||
test_push(&dbuf, 256);
|
||||
test_push(&dbuf, 128);
|
||||
test_push(&dbuf, 17);
|
||||
GUF_TEST_CHECK(dbuf.size == (256 + 128 + 17), "run(): size after push", this);
|
||||
|
||||
dbuf_int_free(&dbuf, NULL);
|
||||
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL, "run(): size after free", this);
|
||||
|
||||
dbuf_int_init(&dbuf, 24, &guf_allocator_libc);
|
||||
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 24 && dbuf.data, "run(): capacity after init with capacity", this);
|
||||
|
||||
test_push(&dbuf, 365);
|
||||
test_push(&dbuf, 4);
|
||||
test_push(&dbuf, 25);
|
||||
test_push(&dbuf, 64);
|
||||
GUF_TEST_CHECK(dbuf.size == (365 + 4 + 25 + 64), "run(): size after push", this);
|
||||
|
||||
dbuf_int_free(&dbuf, NULL);
|
||||
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL, "run(): size after free", this);
|
||||
|
||||
test_insert_remove(4);
|
||||
test_insert_remove(5);
|
||||
test_insert_remove(6);
|
||||
test_insert_remove(7);
|
||||
test_insert_remove(8);
|
||||
test_insert_remove(9);
|
||||
test_insert_remove(10);
|
||||
test_insert_remove(11);
|
||||
test_insert_remove(16);
|
||||
test_insert_remove(17);
|
||||
test_insert_remove(31);
|
||||
test_insert_remove(32);
|
||||
test_insert_remove(33);
|
||||
test_insert_remove(64);
|
||||
test_insert_remove(128);
|
||||
test_insert_remove(400);
|
||||
test_insert_remove(401);
|
||||
test_insert_remove(512);
|
||||
test_insert_remove(513);
|
||||
test_insert_remove(601);
|
||||
test_insert_remove(2048);
|
||||
test_insert_remove(2049);
|
||||
|
||||
const auto delta_t = std::chrono::high_resolution_clock::now() - t0;
|
||||
runtime_ms = std::chrono::duration_cast<decltype(runtime_ms)>(delta_t);
|
||||
|
||||
done = true;
|
||||
passed = (num_failed_checks == 0);
|
||||
return passed;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: use std::unordered_set maybe
|
||||
std::unordered_map<std::string, std::unique_ptr<Test>> g_tests {};
|
||||
|
||||
void init_dbuf_tests()
|
||||
{
|
||||
if (std::unique_ptr<Test> test = std::make_unique<DbufIntTest>("DbufIntTest"); test.get() != NULL) {
|
||||
g_tests.insert({test.get()->name, std::move(test)});
|
||||
} else {
|
||||
throw std::runtime_error("test is NULL");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init_dbuf_tests();
|
||||
|
||||
bool all_passed = true;
|
||||
|
||||
for (auto& [name, test] : g_tests) {
|
||||
if (Test *tst = test.get(); test.get() != NULL) {
|
||||
tst->run();
|
||||
std::cout << "- " << *tst << "\n";
|
||||
all_passed = all_passed && tst->passed;
|
||||
} else {
|
||||
throw std::runtime_error("tst is NULL");
|
||||
}
|
||||
}
|
||||
|
||||
return all_passed ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
54
src/test/test.cpp
Normal file
54
src/test/test.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
extern "C" {
|
||||
#include "guf_init.h"
|
||||
}
|
||||
|
||||
#include "test_dbuf.hpp"
|
||||
|
||||
std::unordered_set<std::unique_ptr<Test>> g_tests {};
|
||||
|
||||
void init_tests()
|
||||
{
|
||||
std::unique_ptr<Test> test = std::make_unique<DbufIntTest>("DbufIntTest");
|
||||
GUF_ASSERT_RELEASE(test.get());
|
||||
g_tests.insert(std::move(test));
|
||||
|
||||
test = std::make_unique<DbufCstringTest>("DbufCstringTest");
|
||||
GUF_ASSERT_RELEASE(test.get());
|
||||
g_tests.insert(std::move(test));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init_tests();
|
||||
|
||||
std::cout << "Running " << g_tests.size() << " tests...\n";
|
||||
|
||||
size_t num_passed = 0;
|
||||
for (auto &test : g_tests) {
|
||||
Test *tst = test.get();
|
||||
GUF_ASSERT_RELEASE(tst);
|
||||
tst->before_run();
|
||||
tst->run();
|
||||
tst->after_run();
|
||||
std::cout << "- " << *tst << "\n";
|
||||
if (tst->passed) {
|
||||
++num_passed;
|
||||
}
|
||||
}
|
||||
|
||||
const bool passed_all = (num_passed == g_tests.size());
|
||||
GUF_ASSERT_RELEASE(num_passed <= g_tests.size());
|
||||
|
||||
if (passed_all) {
|
||||
std::cout << "-> SUCCESS: Passed all (" << num_passed << "/" << g_tests.size() << ") tests.\n";
|
||||
} else {
|
||||
std::cout << "-> FAILURE: Failed " << (g_tests.size() - num_passed) << "/" << g_tests.size() << " tests.\n";
|
||||
}
|
||||
|
||||
return passed_all ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
106
src/test/test.hpp
Normal file
106
src/test/test.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef TEST_HPP
|
||||
#define TEST_HPP
|
||||
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
|
||||
#include "guf_common.h"
|
||||
|
||||
#define TEST_CHECK(COND) check((COND), GUF_STRINGIFY(COND), __LINE__, __FILE__)
|
||||
|
||||
struct Test
|
||||
{
|
||||
private:
|
||||
|
||||
std::chrono::steady_clock::time_point time_start, time_end;
|
||||
|
||||
protected:
|
||||
|
||||
std::stack<std::string> check_name_stack;
|
||||
std::string full_check_name = "";
|
||||
|
||||
void push_check_name(const std::string& check_name)
|
||||
{
|
||||
check_name_stack.push(check_name);
|
||||
full_check_name = full_check_name + "::" + check_name;
|
||||
}
|
||||
|
||||
void pop_check_name()
|
||||
{
|
||||
const size_t sep_idx = full_check_name.rfind("::");
|
||||
GUF_ASSERT_RELEASE(sep_idx != std::string::npos);
|
||||
full_check_name = full_check_name.substr(0, sep_idx);
|
||||
check_name_stack.pop();
|
||||
}
|
||||
|
||||
bool check(bool cond, std::string_view msg, size_t line, std::string_view fname)
|
||||
{
|
||||
if (!cond) {
|
||||
std::cerr << name << full_check_name << ": ";
|
||||
std::cerr << "FAILED CHECK (" << msg << ") on line " << line << " in file " << fname << "\n"; \
|
||||
++num_failed_checks;
|
||||
} else {
|
||||
++num_passed_checks;
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
const std::string name {};
|
||||
std::chrono::duration<float, std::milli> runtime_ms {0};
|
||||
bool passed {false}, done {false};
|
||||
size_t num_failed_checks {0}, num_passed_checks {0};
|
||||
|
||||
Test(const std::string& name) : name{name} {}
|
||||
virtual ~Test() = default;
|
||||
|
||||
size_t total_checks() const
|
||||
{
|
||||
return num_passed_checks + num_failed_checks;
|
||||
}
|
||||
|
||||
virtual bool run() = 0;
|
||||
|
||||
void before_run()
|
||||
{
|
||||
time_start = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void after_run()
|
||||
{
|
||||
time_end = std::chrono::high_resolution_clock::now();
|
||||
runtime_ms = std::chrono::duration_cast<decltype(runtime_ms)>(time_end - time_start);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream &os, const Test& tst)
|
||||
{
|
||||
std::ios_base::fmtflags os_flags = os.flags();
|
||||
std::streamsize os_precision = os.precision();
|
||||
|
||||
os << tst.name << ": " << (tst.passed ? "PASS" : "FAIL");
|
||||
os << std::fixed << std::setprecision(2);
|
||||
os << " (" << tst.num_passed_checks << "/" << tst.total_checks() << ") in " << tst.runtime_ms.count() << " ms";
|
||||
|
||||
os.precision(os_precision);
|
||||
os.flags(os_flags);
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<std::unique_ptr<Test>> {
|
||||
std::size_t operator()(const std::unique_ptr<Test>& test) const
|
||||
{
|
||||
if (test.get() == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return std::hash<std::string>()(test.get()->name);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
447
src/test/test_dbuf.hpp
Normal file
447
src/test/test_dbuf.hpp
Normal file
@ -0,0 +1,447 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "test.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "guf_alloc_libc.h"
|
||||
#include "guf_dbuf_impl.h"
|
||||
}
|
||||
|
||||
struct DbufIntTest : public Test
|
||||
{
|
||||
DbufIntTest(std::string name) : Test(name) {};
|
||||
|
||||
private:
|
||||
|
||||
std::vector<int> dbuf_to_vec(dbuf_int *dbuf)
|
||||
{
|
||||
std::vector<int> vec;
|
||||
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
|
||||
vec.push_back(*it.ptr);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void test_push(dbuf_int *dbuf, int n)
|
||||
{
|
||||
std::vector<int> vec = dbuf_to_vec(dbuf);
|
||||
|
||||
TEST_CHECK(std::ssize(vec) == dbuf->size);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dbuf_int_push_val(dbuf, i);
|
||||
vec.push_back(i);
|
||||
TEST_CHECK(*dbuf_int_back(dbuf) == vec.back());
|
||||
}
|
||||
|
||||
ptrdiff_t i = 0;
|
||||
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
|
||||
TEST_CHECK(*it.ptr == vec.at(i++));
|
||||
}
|
||||
TEST_CHECK(i == dbuf->size);
|
||||
|
||||
i = dbuf->size - 1;
|
||||
GUF_CNT_FOREACH_REVERSE(dbuf, dbuf_int, rit) {
|
||||
TEST_CHECK(*rit.ptr == vec.at(i--));
|
||||
}
|
||||
TEST_CHECK(i == -1);
|
||||
|
||||
}
|
||||
|
||||
void test_insert_remove(int n)
|
||||
{
|
||||
dbuf_int dbuf = {};
|
||||
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
|
||||
std::vector<int> vec = dbuf_to_vec(&dbuf);
|
||||
|
||||
guf_err err = GUF_ERR_NONE;
|
||||
dbuf_int_try_erase(&dbuf, 0, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_erase(&dbuf, 12, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_front(&dbuf, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_back(&dbuf, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_int_try_at(&dbuf, 0, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dbuf_int_insert_val(&dbuf, i, i);
|
||||
dbuf_int_insert_val(&dbuf, i * 2, 0);
|
||||
dbuf_int_insert_val(&dbuf, i * 4, dbuf.size);
|
||||
|
||||
vec.insert(vec.begin() + i, i);
|
||||
vec.insert(vec.begin(), i * 2);
|
||||
vec.insert(vec.end(), i * 4);
|
||||
}
|
||||
TEST_CHECK(std::ssize(vec) == dbuf.size);
|
||||
|
||||
// Iterate
|
||||
dbuf_int_iter it_dbuf = dbuf_int_begin(&dbuf);
|
||||
std::vector<int>::const_iterator it_vec = vec.begin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
|
||||
TEST_CHECK(*it_dbuf.ptr == *it_vec);
|
||||
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 1);
|
||||
std::advance(it_vec, 1);
|
||||
}
|
||||
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end());
|
||||
|
||||
// Step iterate.
|
||||
it_dbuf = dbuf_int_begin(&dbuf);
|
||||
it_vec = vec.begin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
|
||||
TEST_CHECK(*it_dbuf.ptr == *it_vec);
|
||||
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 7);
|
||||
|
||||
if (dbuf_int_iter_is_end(&dbuf, it_dbuf)) {
|
||||
it_vec = vec.end();
|
||||
} else {
|
||||
std::advance(it_vec, 7);
|
||||
}
|
||||
}
|
||||
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end());
|
||||
|
||||
// Reverse iterate.
|
||||
dbuf_int_iter rit_dbuf = dbuf_int_rbegin(&dbuf);
|
||||
std::vector<int>::const_reverse_iterator rit_vec = vec.crbegin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
|
||||
TEST_CHECK(*rit_dbuf.ptr == *rit_vec);
|
||||
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 1);
|
||||
std::advance(rit_vec, 1);
|
||||
}
|
||||
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend());
|
||||
|
||||
// Reverse iterate step.
|
||||
rit_dbuf = dbuf_int_rbegin(&dbuf);
|
||||
rit_vec = vec.crbegin();
|
||||
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
|
||||
TEST_CHECK(*rit_dbuf.ptr == *rit_vec);
|
||||
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 4);
|
||||
if (dbuf_int_iter_is_end(&dbuf, rit_dbuf)) {
|
||||
rit_vec = vec.rend();
|
||||
} else {
|
||||
std::advance(rit_vec, 4);
|
||||
}
|
||||
}
|
||||
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend());
|
||||
|
||||
TEST_CHECK(dbuf.size == std::ssize(vec));
|
||||
|
||||
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
|
||||
dbuf_int_erase(&dbuf, i);
|
||||
dbuf_int_erase(&dbuf, 0);
|
||||
dbuf_int_pop(&dbuf);
|
||||
|
||||
vec.erase(vec.begin() + i);
|
||||
vec.erase(vec.begin() + 0);
|
||||
vec.pop_back();
|
||||
}
|
||||
|
||||
TEST_CHECK(dbuf.size == std::ssize(vec));
|
||||
|
||||
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
|
||||
TEST_CHECK(*dbuf_int_at(&dbuf, i) == vec.at(i));
|
||||
}
|
||||
|
||||
const ptrdiff_t size = dbuf.size;
|
||||
for (ptrdiff_t i = 0; i < size; ++i) {
|
||||
int a = dbuf_int_pop_move(&dbuf);
|
||||
int b = vec.back();
|
||||
TEST_CHECK(a == b);
|
||||
vec.pop_back();
|
||||
}
|
||||
TEST_CHECK(dbuf.size == 0 && vec.size() == 0);
|
||||
|
||||
dbuf_int_free(&dbuf, NULL);
|
||||
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && !dbuf.data);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool run() override
|
||||
{
|
||||
if (done) {
|
||||
return passed;
|
||||
}
|
||||
|
||||
dbuf_int dbuf {};
|
||||
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
|
||||
|
||||
push_check_name("test_push");
|
||||
|
||||
test_push(&dbuf, 256);
|
||||
test_push(&dbuf, 128);
|
||||
test_push(&dbuf, 17);
|
||||
TEST_CHECK(dbuf.size == (256 + 128 + 17));
|
||||
|
||||
dbuf_int_free(&dbuf, NULL);
|
||||
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL);
|
||||
|
||||
dbuf_int_init(&dbuf, 24, &guf_allocator_libc);
|
||||
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 24 && dbuf.data);
|
||||
|
||||
test_push(&dbuf, 365);
|
||||
test_push(&dbuf, 4);
|
||||
test_push(&dbuf, 25);
|
||||
test_push(&dbuf, 64);
|
||||
TEST_CHECK(dbuf.size == (365 + 4 + 25 + 64));
|
||||
|
||||
dbuf_int_free(&dbuf, NULL);
|
||||
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL);
|
||||
|
||||
pop_check_name();
|
||||
|
||||
push_check_name("insert_remove");
|
||||
|
||||
for (int n = 0; n <= 128; ++n) {
|
||||
test_insert_remove(n);
|
||||
}
|
||||
test_insert_remove(400);
|
||||
test_insert_remove(401);
|
||||
test_insert_remove(512);
|
||||
test_insert_remove(513);
|
||||
test_insert_remove(601);
|
||||
test_insert_remove(2048);
|
||||
test_insert_remove(2049);
|
||||
|
||||
pop_check_name();
|
||||
|
||||
done = true;
|
||||
passed = (num_failed_checks == 0);
|
||||
return passed;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct DbufCstringTest : public Test
|
||||
{
|
||||
DbufCstringTest(std::string name) : Test(name) {};
|
||||
|
||||
private:
|
||||
|
||||
void test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step = 1)
|
||||
{
|
||||
GUF_ASSERT_RELEASE(str_dbuf);
|
||||
if (step <= 0) {
|
||||
step = 1;
|
||||
}
|
||||
|
||||
ptrdiff_t i = 0;
|
||||
GUF_CNT_FOREACH(str_dbuf, dbuf_heap_cstr, it) {
|
||||
char *str = *it.ptr;
|
||||
TEST_CHECK(str_vec.at(i) == str);
|
||||
++i;
|
||||
}
|
||||
TEST_CHECK(i == str_dbuf->size);
|
||||
|
||||
i = str_dbuf->size - 1;
|
||||
GUF_CNT_FOREACH_REVERSE(str_dbuf, dbuf_heap_cstr, rit) {
|
||||
char *str = *rit.ptr;
|
||||
TEST_CHECK(str_vec.at(i) == str);
|
||||
--i;
|
||||
}
|
||||
TEST_CHECK(i == -1);
|
||||
|
||||
dbuf_heap_cstr_iter it_dbuf = dbuf_heap_cstr_begin(str_dbuf);
|
||||
std::vector<std::string>::iterator it_vec = str_vec.begin();
|
||||
while (!dbuf_heap_cstr_iter_is_end(str_dbuf, it_dbuf)) {
|
||||
TEST_CHECK(it_vec != str_vec.end());
|
||||
TEST_CHECK(*it_vec == *it_dbuf.ptr);
|
||||
it_dbuf = dbuf_heap_cstr_iter_next(str_dbuf, it_dbuf, step);
|
||||
if (!dbuf_heap_cstr_iter_is_end(str_dbuf, it_dbuf)) {
|
||||
std::advance(it_vec, step);
|
||||
} else {
|
||||
it_vec = str_vec.end();
|
||||
}
|
||||
}
|
||||
TEST_CHECK(dbuf_heap_cstr_iter_is_end(str_dbuf, it_dbuf) && it_vec == str_vec.end());
|
||||
|
||||
dbuf_heap_cstr_iter rit_dbuf = dbuf_heap_cstr_rbegin(str_dbuf);
|
||||
std::vector<std::string>::reverse_iterator rit_vec = str_vec.rbegin();
|
||||
while (!dbuf_heap_cstr_iter_is_end(str_dbuf, rit_dbuf)) {
|
||||
TEST_CHECK(rit_vec != str_vec.rend());
|
||||
TEST_CHECK(*rit_vec == *rit_dbuf.ptr);
|
||||
rit_dbuf = dbuf_heap_cstr_iter_next(str_dbuf, rit_dbuf, step);
|
||||
if (!dbuf_heap_cstr_iter_is_end(str_dbuf, rit_dbuf)) {
|
||||
std::advance(rit_vec, step);
|
||||
} else {
|
||||
rit_vec = str_vec.rend();
|
||||
}
|
||||
}
|
||||
TEST_CHECK(dbuf_heap_cstr_iter_is_end(str_dbuf, rit_dbuf) && rit_vec == str_vec.rend());
|
||||
|
||||
for (i = 0; i < str_dbuf->size; ++i) {
|
||||
char *str = *dbuf_heap_cstr_at(str_dbuf, i);
|
||||
TEST_CHECK(str_vec.at(i) == str);
|
||||
}
|
||||
}
|
||||
|
||||
void test_push_insert_erase(int n, ptrdiff_t start_cap = 0)
|
||||
{
|
||||
std::vector<std::string> str_vec;
|
||||
dbuf_heap_cstr str_dbuf {};
|
||||
dbuf_heap_cstr_init(&str_dbuf, start_cap, &guf_allocator_libc);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
constexpr int BUF_SZ = 128;
|
||||
char buf[BUF_SZ];
|
||||
memset(buf, '\0', BUF_SZ);
|
||||
snprintf(buf, BUF_SZ, "This is string number %d", i);
|
||||
guf_cstr_heap str = buf;
|
||||
|
||||
dbuf_heap_cstr_push(&str_dbuf, &str, GUF_CPY_DEEP);
|
||||
dbuf_heap_cstr_push_val_cpy(&str_dbuf, str);
|
||||
char *heap_buf = strdup("Move me plz");
|
||||
dbuf_heap_cstr_push(&str_dbuf, &heap_buf, GUF_CPY_MOVE);
|
||||
TEST_CHECK(heap_buf == NULL);
|
||||
|
||||
TEST_CHECK(strncmp(*dbuf_heap_cstr_back(&str_dbuf), "Move me plz", BUF_SZ) == 0);
|
||||
TEST_CHECK(strncmp(*dbuf_heap_cstr_at(&str_dbuf, str_dbuf.size - 2), buf, BUF_SZ) == 0);
|
||||
TEST_CHECK(strncmp(*dbuf_heap_cstr_at(&str_dbuf, str_dbuf.size - 3), buf, BUF_SZ) == 0);
|
||||
|
||||
str_vec.push_back(std::string{buf});
|
||||
str_vec.push_back(std::string{buf});
|
||||
str_vec.emplace_back("Move me plz");
|
||||
}
|
||||
|
||||
TEST_CHECK(str_dbuf.size == std::ssize(str_vec));
|
||||
TEST_CHECK(str_dbuf.size == 3 * n);
|
||||
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
test_iter(str_vec, &str_dbuf, i);
|
||||
}
|
||||
test_iter(str_vec, &str_dbuf, str_dbuf.size);
|
||||
test_iter(str_vec, &str_dbuf, str_dbuf.size - 1);
|
||||
test_iter(str_vec, &str_dbuf, str_dbuf.size + 1);
|
||||
|
||||
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
|
||||
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
|
||||
}
|
||||
|
||||
// Insert front.
|
||||
for (ptrdiff_t i = 0; i < 16; ++i) {
|
||||
char str[] = "front";
|
||||
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, 0);
|
||||
str_vec.insert(str_vec.begin(), std::string{str});
|
||||
}
|
||||
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
|
||||
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
|
||||
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
|
||||
}
|
||||
// Insert back.
|
||||
for (ptrdiff_t i = 0; i < 16; ++i) {
|
||||
char str[] = "front";
|
||||
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size);
|
||||
str_vec.insert(str_vec.end(), std::string{str});
|
||||
}
|
||||
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
|
||||
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
|
||||
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
|
||||
}
|
||||
|
||||
// Insert at i.
|
||||
char str[] = "guf";
|
||||
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size / 2);
|
||||
str_vec.insert(str_vec.begin() + str_vec.size() / 2, str);
|
||||
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size / 4);
|
||||
str_vec.insert(str_vec.begin() + str_vec.size() / 4, str);
|
||||
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, 1);
|
||||
str_vec.insert(str_vec.begin() + 1, str);
|
||||
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size - 1);
|
||||
str_vec.insert(str_vec.begin() + (str_vec.size() - 1), str);
|
||||
|
||||
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
|
||||
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
|
||||
}
|
||||
|
||||
guf_err err = GUF_ERR_NONE;
|
||||
dbuf_heap_cstr_try_insert_val_cpy(&str_dbuf, str, str_dbuf.size + 1, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_heap_cstr_try_insert_val_cpy(&str_dbuf, str, -1, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
err = GUF_ERR_NONE;
|
||||
dbuf_heap_cstr_try_insert_val_cpy(&str_dbuf, str, str_dbuf.size + 2, &err);
|
||||
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
|
||||
|
||||
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
|
||||
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
|
||||
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
|
||||
}
|
||||
|
||||
if (str_dbuf.size) {
|
||||
dbuf_heap_cstr_erase(&str_dbuf, str_dbuf.size - 1);
|
||||
str_vec.erase(str_vec.end() - 1);
|
||||
}
|
||||
|
||||
ptrdiff_t to_rem = 8;
|
||||
while (str_dbuf.size && to_rem--) {
|
||||
dbuf_heap_cstr_erase(&str_dbuf, 0);
|
||||
str_vec.erase(str_vec.begin());
|
||||
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
|
||||
if (str_dbuf.size) {
|
||||
dbuf_heap_cstr_pop(&str_dbuf);
|
||||
str_vec.pop_back();
|
||||
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
|
||||
}
|
||||
if (str_dbuf.size) {
|
||||
dbuf_heap_cstr_erase(&str_dbuf, str_dbuf.size / 2);
|
||||
str_vec.erase(str_vec.begin() + (str_vec.size() / 2));
|
||||
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
|
||||
}
|
||||
}
|
||||
|
||||
dbuf_heap_cstr_free(&str_dbuf, NULL);
|
||||
TEST_CHECK(str_dbuf.size == 0 && str_dbuf.capacity == 0 && !str_dbuf.data);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool run()
|
||||
{
|
||||
for (int i = 1; i <= 32; ++i) {
|
||||
test_push_insert_erase(i);
|
||||
test_push_insert_erase(i, i - 1);
|
||||
test_push_insert_erase(i, i + 1);
|
||||
test_push_insert_erase(i, i);
|
||||
test_push_insert_erase(i, i / 2);
|
||||
}
|
||||
test_push_insert_erase(2048);
|
||||
test_push_insert_erase(2048, 11);
|
||||
|
||||
dbuf_heap_cstr str_dbuf = {};
|
||||
dbuf_heap_cstr_init(&str_dbuf, 0, &guf_allocator_libc);
|
||||
std::vector<std::string> str_vec {};
|
||||
|
||||
for (int i = 0; i < 512; ++i) {
|
||||
char buf[128];
|
||||
memset(buf, '\0', GUF_STATIC_BUF_SIZE(buf));
|
||||
snprintf(buf, GUF_STATIC_BUF_SIZE(buf), "This is a pretty guf string (number %d)", i);
|
||||
guf_cstr_heap str = buf;
|
||||
dbuf_heap_cstr_push(&str_dbuf, &str, GUF_CPY_DEEP);
|
||||
str_vec.push_back(std::string{buf});
|
||||
}
|
||||
for (int i = 0; i < str_dbuf.size + 16; ++i) {
|
||||
test_iter(str_vec, &str_dbuf, i);
|
||||
}
|
||||
|
||||
dbuf_heap_cstr_free(&str_dbuf, NULL);
|
||||
TEST_CHECK(str_dbuf.size == 0 && str_dbuf.capacity == 0 && !str_dbuf.data);
|
||||
|
||||
done = true;
|
||||
passed = (num_failed_checks == 0);
|
||||
return passed;
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user