libguf/src/test/test_dbuf.cpp
2025-12-21 19:51:42 +01:00

686 lines
24 KiB
C++
Executable File

#include "test_dbuf.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
#include "impls/dbuf_impl.h"
}
/*
DbufIntTest
*/
void DbufIntTest::run()
{
if (done) {
return;
}
// allocator_ctx.tracker.log = fopen("alloc_log.txt", "w");
// allocator_ctx.tracker.err_log = fopen("alloc_err_log.txt", "w");
dbuf_int dbuf {};
dbuf_int_init(&dbuf, 0, &allocator);
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, &allocator);
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();
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
// guf_alloc_tracker_print(&allocator_ctx.tracker, stdout);
// puts("");
// fclose(allocator_ctx.tracker.log);
// fclose(allocator_ctx.tracker.err_log);
}
std::vector<int> DbufIntTest::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 DbufIntTest::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 DbufIntTest::test_insert_remove(int n)
{
dbuf_int dbuf = {};
dbuf_int_init(&dbuf, 0, &allocator);
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);
}
/*
DbufCstringTest
*/
void DbufCstringTest::run()
{
if (done) {
return;
}
// allocator_ctx.tracker.log = fopen("alloc_log.txt", "w");
// allocator_ctx.tracker.err_log = fopen("alloc_err_log.txt", "w");
push_check_name("push_insert_erase");
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, &allocator);
std::vector<std::string> str_vec {};
for (int i = 0; i < 512; ++i) {
char buf[128];
memset(buf, '\0', GUF_ARR_SIZE(buf));
snprintf(buf, GUF_ARR_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);
pop_check_name();
push_check_name("find");
test_find();
test_find(3);
test_find(42);
test_find(129);
pop_check_name();
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
// guf_alloc_tracker_print(&allocator_ctx.tracker, stdout);
// puts("");
// fclose(allocator_ctx.tracker.log);
// fclose(allocator_ctx.tracker.err_log);
}
void DbufCstringTest::test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step)
{
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 DbufCstringTest::test_push_insert_erase(int n, ptrdiff_t start_cap)
{
std::vector<std::string> str_vec;
dbuf_heap_cstr str_dbuf {};
dbuf_heap_cstr_init(&str_dbuf, start_cap, &allocator);
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, (int)str_dbuf.size);
test_iter(str_vec, &str_dbuf, (int)str_dbuf.size - 1);
test_iter(str_vec, &str_dbuf, (int)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);
}
void DbufCstringTest::test_find(int n)
{
if (n < 2) {
n = 2;
}
std::vector<std::string> str_vec {};
dbuf_heap_cstr str_dbuf = {};
dbuf_heap_cstr_init(&str_dbuf, 0, &allocator);
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, "String number %d", i);
dbuf_heap_cstr_push_val_cpy(&str_dbuf, buf);
str_vec.push_back(buf);
}
char *move_me = strdup("Moved string");
dbuf_heap_cstr_push(&str_dbuf, &move_me, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me == NULL);
str_vec.emplace_back("Moved string");
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
char *needle = *dbuf_heap_cstr_at(&str_dbuf, i);
TEST_CHECK(str_vec.at(i) == needle);
TEST_CHECK(dbuf_heap_cstr_contains_val(&str_dbuf, needle));
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), dbuf_heap_cstr_end(&str_dbuf), needle);
TEST_CHECK(!dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin(), str_vec.cend(), needle) != str_vec.end());
dbuf_heap_cstr_iter begin = dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), i);
dbuf_heap_cstr_iter end = dbuf_heap_cstr_end(&str_dbuf);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, begin, end, needle);
TEST_CHECK(!dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin() + i, str_vec.cend(), needle) != str_vec.end());
begin = dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), i + 1);
end = dbuf_heap_cstr_end(&str_dbuf);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, begin, end, needle);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin() + i + 1, str_vec.cend(), needle) == str_vec.end());
// Reverse.
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_rbegin(&str_dbuf), dbuf_heap_cstr_rend(&str_dbuf), needle);
TEST_CHECK(!dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.crbegin(), str_vec.crend(), needle) != str_vec.rend());
}
char needle[] = "Definitely not inside";
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), dbuf_heap_cstr_end(&str_dbuf), needle);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin(), str_vec.cend(), needle) == str_vec.end());
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_rbegin(&str_dbuf), dbuf_heap_cstr_rend(&str_dbuf), needle);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.crbegin(), str_vec.crend(), needle) == str_vec.rend());
char *needle2 = *dbuf_heap_cstr_at(&str_dbuf, 0);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), 1), dbuf_heap_cstr_end(&str_dbuf), needle2);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin() + 1, str_vec.cend(), needle2) == str_vec.end());
needle2 = *dbuf_heap_cstr_back(&str_dbuf);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), 1), dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_end(&str_dbuf), -1), needle2);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.begin(), str_vec.end() - 1, needle2) == (str_vec.end() - 1));
needle2 = *dbuf_heap_cstr_at(&str_dbuf, 0);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), dbuf_heap_cstr_begin(&str_dbuf), needle2);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin(), str_vec.cbegin(), needle2) == str_vec.cbegin());
dbuf_heap_cstr_free(&str_dbuf, NULL);
}
/*
DbufStrTest:
*/
void DbufStrTest::run()
{
test_push_insert_erase(16);
test_push_insert_erase(16, 1);
test_push_insert_erase(16, 15);
test_push_insert_erase(16, 16);
test_push_insert_erase(16, 97);
test_push_insert_erase(16, 256);
test_push_insert_erase(500);
test_push_insert_erase(500, 1);
test_push_insert_erase(500, 499);
test_push_insert_erase(500, 500);
test_push_insert_erase(500, 97);
test_push_insert_erase(500, 256);
}
void DbufStrTest::test_push_insert_erase(size_t n, ptrdiff_t start_cap)
{
dbuf_str strings = start_cap < 0 ? dbuf_str_new(&allocator) : dbuf_str_new_with_capacity(start_cap, &allocator);
std::vector<std::string> strings_cpp {};
guf_libc_alloc_ctx str_allocator_ctx = {
.tracker = guf_alloc_tracker_new(42, "test_push_insert_erase: local str allocator", NULL, NULL),
.zero_init = false
};
guf_allocator str_allocator = guf_libc_allocator_new(&str_allocator_ctx);
for (size_t i = 0; i < n; ++i) {
std::string str;
for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) {
str += (char)(c % 10 + '0');
}
const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1};
guf_str long_str = GUF_STR_UNINITIALISED_CPP, short_str = GUF_STR_UNINITIALISED_CPP;
guf_str_init(&long_str, sv_long, &str_allocator);
guf_str_init(&short_str, sv_short, &str_allocator);
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 1 + (i*3));
// Move
guf_err err;
dbuf_str_try_push(&strings, &long_str, GUF_CPY_MOVE, &err);
TEST_CHECK(err == GUF_ERR_NONE);
dbuf_str_try_push(&strings, &short_str, GUF_CPY_MOVE, &err);
TEST_CHECK(err == GUF_ERR_NONE);
strings_cpp.push_back(str.substr(0, str.size()));
strings_cpp.push_back(str.substr(0, GUF_STR_SSO_BUF_CAP - 1));
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 1 + (i*3));
TEST_CHECK(guf_str_is_uninit(&long_str));
TEST_CHECK(guf_str_is_uninit(&short_str));
// Deep-copy
guf_str_init(&long_str, sv_long, &str_allocator);
guf_str_init(&short_str, sv_short, &str_allocator);
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 2 + (i*3));
dbuf_str_try_push(&strings, &long_str, GUF_CPY_DEEP, &err);
TEST_CHECK(err == GUF_ERR_NONE);
dbuf_str_try_push(&strings, &short_str, GUF_CPY_DEEP, &err);
TEST_CHECK(err == GUF_ERR_NONE);
strings_cpp.push_back(str.substr(0, str.size()));
strings_cpp.push_back(str.substr(0, GUF_STR_SSO_BUF_CAP - 1));
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 + (i*3));
TEST_CHECK(guf_str_is_valid(&long_str) && guf_str_is_valid(&short_str));
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(&long_str), sv_long));
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(&short_str), sv_short));
guf_str_free(&long_str, NULL);
guf_str_free(&short_str, NULL);
TEST_CHECK(str_allocator_ctx.tracker.free_count == 1 + (i*1));
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 + (i*3));
}
TEST_CHECK(str_allocator_ctx.tracker.free_count == n);
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 * n);
TEST_CHECK(strings.size == 4 * (ptrdiff_t)n);
TEST_CHECK(strings.size == std::ssize(strings_cpp));
std::string str;
for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) {
str += (char)(c % 10 + '0');
}
const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1};
size_t i = 0;
GUF_CNT_FOREACH(&strings, dbuf_str, it) {
if (TEST_CHECK(it.ptr)) {
const guf_str *s = it.ptr;
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(s), i % 2 == 0 ? sv_long : sv_short));
const guf_str_view sv_cpp = guf_str_view {.str = strings_cpp.at(i).data(), .len = (ptrdiff_t)strings_cpp.at(i).size()};
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(s), sv_cpp));
}
++i;
}
GUF_CNT_FOREACH(&strings, dbuf_str, it) {
if (TEST_CHECK(it.ptr)) {
guf_str *s = it.ptr;
TEST_CHECK(guf_str_append(s, GUF_CSTR_LIT_TO_VIEW_CPP("<END>")));
}
}
std::vector<guf_str_view> delims = {guf_str_view{.str = "<END>", .len = 5}};
i = 0;
GUF_CNT_FOREACH(&strings, dbuf_str, it) {
if (TEST_CHECK(it.ptr)) {
const guf_str *s = it.ptr;
guf_str_tok_state tk_state = guf_str_tok_state_new(guf_str_view_from_str(s), delims.data(), std::ssize(delims), GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
size_t tok_n = 0;
while (guf_str_tok_next(&tk_state, true)) {
TEST_CHECK(guf_str_view_equal_val_arg(tk_state.cur_tok , i % 2 == 0 ? sv_long : sv_short));
TEST_CHECK(guf_str_view_equal_val_arg(tk_state.cur_delim, guf_str_view{.str = "<END>", .len = 5}));
++tok_n;
}
TEST_CHECK(tok_n == 1);
}
++i;
}
dbuf_str_free(&strings, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&str_allocator_ctx.tracker));
}