Add README

This commit is contained in:
zeichensystem 2025-12-23 21:18:21 +01:00 committed by fruit
parent 97f21e24dc
commit 91aeeecc0f
30 changed files with 57 additions and 5196 deletions

View File

@ -4,16 +4,16 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 11)
add_definitions(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON) add_definitions(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON)
if (NOT MSVC) if (NOT MSVC)
set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wsign-promo -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Wno-unused-function) set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Wno-unused-function)
set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wsign-promo -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Woverloaded-virtual -Wredundant-decls -Wctor-dtor-privacy -Wno-unused-function) set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wsign-promo -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Woverloaded-virtual -Wredundant-decls -Wctor-dtor-privacy -Wno-unused-function)
set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og) set(DBG_FLAGS -fsanitize=undefined,address -g3 -Og)
else () else ()
set(WARNING_FLAGS_C /W4) set(WARNING_FLAGS_C /W4)
set(WARNING_FLAGS_CXX /W4) set(WARNING_FLAGS_CXX /W4)
@ -45,7 +45,7 @@ set(LIBGUF_IMPLS ${CMAKE_CURRENT_SOURCE_DIR}/libguf_impls/guf_alloc_libc_impl.c
foreach(current_target IN LISTS TARGETS) foreach(current_target IN LISTS TARGETS)
add_executable(${current_target} ${current_target}/${current_target}.c ${LIBGUF_IMPLS}) add_executable(${current_target} ${current_target}/${current_target}.c ${LIBGUF_IMPLS})
set_target_properties(${current_target} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) set_target_properties(${current_target} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
target_include_directories(${current_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_SOURCE_DIR}/libguf/src/") target_include_directories(${current_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_SOURCE_DIR}/libguf/")
target_compile_options(${current_target} PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>:${DBG_FLAGS}>) target_compile_options(${current_target} PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>:${DBG_FLAGS}>)
target_link_options(${current_target} PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>:${DBG_FLAGS}>) target_link_options(${current_target} PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>:${DBG_FLAGS}>)

53
README.md Executable file
View File

@ -0,0 +1,53 @@
# Advent of Code 2025
C99 solutions to the puzzles of [Advent of Code 2025](https://adventofcode.com/2025) using my [libguf](https://git.tilde.town/fruit/libguf).
(Previous years: [2024](https://github.com/zeichensystem/advent-of-code-2024), [2023](https://github.com/zeichensystem/advent-of-code-2023))
As the creator of Advent of Code does not allow/encourage sharing your own puzzle inputs, I only put example puzzle inputs into [input/](input/) publicly, so you can at least run those if you don't have your own puzzle inputs.
My answers are correct for my puzzle inputs (and for the example inputs), but in my experience it is definitely possible to come up with *incorrect* code which may produce the *correct* answers for your own puzzle input, but which might not work for all other possible/legal puzzle inputs. (I put the answers to my puzzle inputs and to the example inputs as comments at the very top of the source-files for each day.)
## How to build and run
You need at least a C99 compiler and cmake.
### 1. Configure cmake and generate build systems
Navigate to the top level of this repository (the directory where [CMakeLists.txt](CMakeLists.txt) is located) and create your build directories, for instance:
`mkdir -p build/Release build/Debug`
Then, run:
`cmake -DCMAKE_BUILD_TYPE=Release -S . -B build/Release`
`cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build/Debug`
to generate the release build system into your build directory `build/Release` (and the debug build system into your build directory `build/Debug`). (If you do not run the commands at the top level of this repository, you have to run them with `-S path-to-this-repo` instead of `-S .` like above.)
You can also set `-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=` if you want to use a custom output directory for the compiled binaries; by default, they will be put into the `bin/` directory at the root of this repository.
### 2. Build
Run
`cmake --build build/Release --target day-nn`
to build the release-mode executable for `day-nn` (or run `cmake --build build/Release` to build the release-mode executables for all days, but this might take a while).
The resulting executable will be called `day-nn` (or `day-nn_dbg` for debug builds). By default, it will be put into the `bin/` directory at the root of this repository (or into the directory you specified with `-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=` in step 1).
(For example: Run `cmake --build build/Release --target day-03` to build the release-mode executable which solves Day 3. It will be put into `bin/day-03` by default. Or run `cmake --build build/Debug --target day-03` to build the debug-mode executable `bin/day-03_dbg` instead, assuming you generated the debug build system into 'build/Debug' in step 1.)
### 3. Run
Run `bin/day-nn input_day_nn.txt` to compute and print the solutions (part 1 and part 2) for *day-nn* (with `input_day_nn.txt` being your puzzle input for that day). If you don't have your own puzzle inputs, you can use the example inputs from the [input/](input/) directory.
```
Usage: day-nn [-help] puzzle_input [-v]
-v: use verbose output (optional)
-help: print this help, ignore the rest, and quit (optional)
puzzle_input: your puzzle input file (optional/ignored if -help is used)
```
You can also build and run *day-nn* in one step with `cmake --build build/Release --target run-day-nn`, but this assumes you have saved your puzzle input for *day-nn* as `input/day-nn.txt` within this repository.
If you don't have your own puzzle inputs but still want to test the executable for *day-nn*, you can build and run with `cmake --build build/Release --target run-day-nn-example` (which uses the puzzle's example input `input/day-nn-example.txt` automatically).
For example: `cmake --build build/Release --target run-day-02-example` to build and run the release-mode executable for *day-02* with the example input [input/day-02-example.txt](input/day-02-example.txt)

File diff suppressed because it is too large Load Diff

View File

@ -1,67 +0,0 @@
„Ich weiß nicht“, rief ich ohne Klang „ich weiß ja nicht. Wenn
niemand kommt, dann kommt eben niemand. Ich habe niemandem etwas
Böses getan, niemand hat mir etwas Böses getan, niemand aber will
mir helfen. Lauter niemand. Aber so ist es doch nicht. Nur daß mir
niemand hilft —, sonst wäre lauter niemand hübsch. Ich würde ganz
gern — warum denn nicht — einen Ausflug mit einer Gesellschaft von
lauter Niemand machen. Natürlich ins Gebirge, wohin denn sonst? Wie
sich diese Niemand aneinander drängen, diese vielen quer gestreckten
und eingehängten Arme, diese vielen Füße, durch winzige Schritte
getrennt! Versteht sich, daß alle in Frack sind. Wir gehen so lala,
der Wind fährt durch die Lücken, die wir und unsere Gliedmaßen offen
lassen. Die Hälse werden im Gebirge frei! Es ist ein Wunder, daß
wir nicht singen.“
Det var i den Tid, jeg gik omkring og sulted i Kristiania, denne forunderlige By,
som ingen forlader, før han har fået Mærker af den . . . .
Jeg ligger vågen på min Kvist og hører en Klokke nedenunder mig slå seks Slag; det var allerede ganske lyst,
og Folk begyndte at færdes op og ned i Trapperne. Nede ved Døren, hvor mit Rum var tapetseret med gamle Numre
af »Morgenbladet«, kunde jeg så tydelig se en Bekendtgørelse fra Fyrdirektøren, og lidt tilvenstre derfra et fedt,
bugnende Avertissement fra Bager Fabian Olsen om nybagt Brød.
The quick brown fox jumps over the lazy dog.
Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.
Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.
Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία.
El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.
Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en
canoë au delà des îles, près du mälström où brûlent les novæ.
D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh.
Árvíztűrő tükörfúrógép.
Pchnąć w tę łódź jeża lub ośm skrzyń fig.
Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa.
В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!
Pijamalı hasta, yağız şoföre çabucak güvendi.
Albert osti fagotin ja töräytti puhkuvan melodian.
דג סקרן שט בים מאוכזב ולפתע מצא חברה
نص حكيم له سر قاطع وذو شأن عظيم مكتوب على ثوب أخضر ومغلف بجلد أزرق
بر اثر چنین تلقین و شستشوی مغزی جامعی، سطح و پایهٔ ذهن و فهم و نظر بعضی اشخاص واژگونه و معکوس می‌شود
키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다.
いろはにほへとちりぬるを
わかよたれそつねならむ
うゐのおくやまけふこえて
あさきゆめみしゑひもせす
イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン
ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬

View File

@ -1,320 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "guf_init.h" /* Must be included once (or compiled in a separate .c file and linked) */
#define GUF_ALLOC_LIBC_IMPL_STATIC
#include "guf_alloc_libc.h"
#include "guf_cstr.h"
#include "guf_linalg.h"
#include "guf_utils.h"
#define GUF_T float
#define GUF_SORT_IMPL_STATIC
#include "guf_sort.h"
#define GUF_T int
#define GUF_SORT_IMPL_STATIC
#include "guf_sort.h"
#define GUF_DBUF_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_DBUF_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#define GUF_DBUF_IMPL_STATIC
// #define GUF_CNT_WITH_ELEM_CTX
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_DBUF_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
#define GUF_RAND_IMPL_STATIC
// #define GUF_RAND_32_BIT
#include "guf_rand.h"
#include "impls/dict_impl.h"
int main(void)
{
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
guf_alloc_tracker_init(&allocator_ctx.tracker, 1, "example_allocator", NULL, NULL);
allocator_ctx.zero_init = false;
guf_libc_allocator_init(&allocator, &allocator_ctx);
printf("libguf example: " GUF_PLATFORM_STRING "\n");
guf_platform_assert_endianness();
guf_platform_assert_native_word_bits();
guf_allocator zero_init_allocator;
guf_libc_alloc_ctx zero_init_allocator_ctx;
guf_alloc_tracker_init(&zero_init_allocator_ctx.tracker, 2, "example_zero_init_allocator", stdout, stderr);
zero_init_allocator_ctx.zero_init = true;
guf_libc_allocator_init(&zero_init_allocator, &zero_init_allocator_ctx);
dict_cstr_int ht;
dict_cstr_int_init(&ht, &zero_init_allocator);
dict_cstr_int_insert_val_arg(&ht, "Hello", 42, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_cstr_int_insert_val_arg(&ht, "World", 64, GUF_CPY_VALUE, GUF_CPY_VALUE);
int kv_iter = 0;
GUF_CNT_FOREACH(&ht, dict_cstr_int, kv_it) {
printf("%d: %s -> %d\n", kv_iter++, kv_it.ptr->key, kv_it.ptr->val);
}
guf_cstr_const key = "World";
int *res = dict_cstr_int_at_val_arg(&ht, "World");
if (res) {
printf("%s: %d\n", key, *res);
} else {
printf("key '%s' not found\n", key);
}
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "World"));
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "Hello"));
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "hello") == NULL);
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "") == NULL);
GUF_ASSERT(dict_cstr_int_contains_val_arg(&ht, "World"));
GUF_ASSERT(dict_cstr_int_contains_val_arg(&ht, "Hello"));
const int ht_needle_val = 64;
const dict_cstr_int_iter ht_it = dict_cstr_int_find_val(&ht, dict_cstr_int_begin(&ht), dict_cstr_int_end(&ht), &ht_needle_val);
if (!dict_cstr_int_iter_is_end(&ht, ht_it)) {
printf("found value %d (key %s)\n", ht_needle_val, ht_it.ptr->key);
}
dict_cstr_int_free(&ht, NULL);
GUF_CNT_LIFETIME_BLOCK(dbuf_float, floats, {
floats = dbuf_float_new(&allocator);
for (int i = 0; i <= 16; ++i) {
dbuf_float_push_val(&floats, i % 2 ? (float)i * -2.f : (float)i * 2.f);
}
// float *tmp = test_allocator.alloc(floats.size * sizeof(float), &test_allocator_ctx);
// float *res = float_arr_merge_sort(floats.data, tmp, floats.size, GUF_SORT_ASCENDING, NULL);
// test_allocator.free(tmp, floats.size * sizeof(float), &test_allocator_ctx);
// GUF_ASSERT_RELEASE(res == floats.data);
float_arr_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL);
GUF_ASSERT_RELEASE(float_arr_is_sorted(floats.data, floats.size, GUF_SORT_ASCENDING, NULL));
GUF_CNT_FOREACH(&floats, dbuf_float, it) {
printf("float: %f\n", (double)*it.ptr);
}
})
dbuf_heap_cstr strings = dbuf_heap_cstr_new(&allocator);
dbuf_heap_cstr_push_val_cpy(&strings, "Foo 1");
dbuf_heap_cstr_push_val_cpy(&strings, "Bar 2");
char *move_me = guf_cstr_dup("Baz 3");
dbuf_heap_cstr_push(&strings, &move_me, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me == NULL);
dbuf_heap_cstr_push_val_cpy(&strings, "Boz 4");
char *findme = "Baz 3";
dbuf_heap_cstr_iter beg = dbuf_heap_cstr_begin(&strings);
dbuf_heap_cstr_iter end = dbuf_heap_cstr_end(&strings);
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find(&strings, beg, end, &findme);
if (!dbuf_heap_cstr_iter_is_end(&strings, fnd_it)) {
printf("%s found in range [%td, %td) at idx %td\n", findme, dbuf_heap_cstr_iter_to_idx(&strings, beg), dbuf_heap_cstr_iter_to_idx(&strings, end), dbuf_heap_cstr_iter_to_idx(&strings, fnd_it));
} else {
printf("%s not found in range [%td, %td) at idx %td\n", findme, dbuf_heap_cstr_iter_to_idx(&strings, beg), dbuf_heap_cstr_iter_to_idx(&strings, end), dbuf_heap_cstr_iter_to_idx(&strings, fnd_it));
}
if (dbuf_heap_cstr_contains_val(&strings, "Baz 3")) {
printf("contains\n");
} else {
printf("does not contain\n");
}
GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) {
printf("%s\n", *it.ptr);
}
dbuf_heap_cstr_free(&strings, NULL);
dbuf_const_cstr const_strings = dbuf_const_cstr_new(&allocator);
dbuf_const_cstr_push_val(&const_strings, "Const 1");
dbuf_const_cstr_push_val(&const_strings, "Const 2");
const char *foo = "Const 3";
dbuf_const_cstr_push(&const_strings, &foo, GUF_CPY_VALUE);
dbuf_const_cstr_iter found_it = dbuf_const_cstr_find(&const_strings, dbuf_const_cstr_begin(&const_strings), dbuf_const_cstr_end(&const_strings), &foo);
if (found_it.ptr != dbuf_const_cstr_end(&const_strings).ptr) {
*found_it.ptr = "Found!";
}
GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) {
printf("%s\n", *it.ptr);
}
dbuf_const_cstr_free(&const_strings, NULL);
dbuf_int integers = dbuf_int_new(&allocator);
dbuf_int_push_val(&integers, 420);
dbuf_int_push_val(&integers, 520);
dbuf_int_push_val(&integers, 620);
dbuf_int_push_val(&integers, 720);
dbuf_int_push_val(&integers, 820);
guf_err err;
dbuf_int_try_at(&integers, 16, &err);
if (err) {
printf("%s %s\n", guf_err_to_str(err), GUF_ERR_MSG_EMPTY());
}
int i = 0;
GUF_DBUF_FOREACH(integers, int, elem) {
printf("elem %d: %d\n", i, *elem);
++i;
}
GUF_CNT_FOREACH(&integers, dbuf_int, it) {
printf("it-elem: %d", *it.ptr);
if (dbuf_int_iter_next(&integers, it, 1).ptr != dbuf_int_end(&integers).ptr) {
printf(", it-next: %d", *dbuf_int_iter_next(&integers, it, 1).ptr);
}
if (dbuf_int_iter_next(&integers, it, -1).ptr != dbuf_int_end(&integers).ptr) {
printf(", it-prev: %d", *dbuf_int_iter_next(&integers, it, -1).ptr);
}
printf("\n");
}
for (dbuf_int_iter it = dbuf_int_begin(&integers); it.ptr != dbuf_int_end(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) {
printf("every other: %d\n", *it.ptr);
}
for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 1)) {
printf("reverse: %d\n", *it.ptr);
}
for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) {
printf("every other reverse: %d (idx %td)\n", *it.ptr, dbuf_int_iter_to_idx(&integers, it));
}
dbuf_int_free(&integers, NULL);
printf("\n");
guf_randstate rng;
guf_randstate_init(&rng, (guf_rand_seed_t)time(NULL));
int heads = 0, tails = 0;
int throws = 10;
for (i = 0; i < throws; ++i) {
bool is_head = guf_rand_flip(&rng);
if (is_head) {
puts("head");
++heads;
} else {
puts("tail");
++tails;
}
}
printf("n: %d\nheads: %d\ntails: %d\n", throws, heads, tails);
int result[256];
memset(result, 0, sizeof result);
for (int n = 0; n < 32000; ++n) {
float r = roundf(guf_rand_normal_sample_one_f32(&rng, 100, 15));
r = guf_clamp_f32(r, 0, 255);
result[(int)r] += 1;
}
for (size_t n = 60; n <= 140; ++n) {
printf("%zu:\t", n);
for (int j = 0; j < result[n] / 8; ++j) {
putc('#', stdout);
}
puts("");
}
for (float angle = 0; angle <= 8.f * GUF_PI_F32; angle += 0.001f) {
guf_quaternion rotq = guf_quaternion_from_axis_angle(angle, guf_vec3_normalised((guf_vec3){-2324234.3f, 1.4f, -1.3f}));
guf_mat4x4 rotmat, rotmat_inv;
guf_mat4x4_init_from_quaternion(&rotmat, rotq);
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat, &rotmat_inv))
guf_mat4x4_set_trans(&rotmat, (guf_vec3){42.1234f, -512.2f, 3.1415926f});
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat, &rotmat_inv));
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat_inv, &rotmat));
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat, &rotmat_inv));
}
guf_quaternion q = guf_quaternion_from_axis_angle(GUF_PI_F32 / 8.f, guf_vec3_normalised((guf_vec3){0.3f, 10.2f, -25.f}));
guf_mat4x4 mat;
guf_mat4x4_init_from_quaternion(&mat, q);
guf_mat4x4_set_trans(&mat, (guf_vec3){42.1234f, -512.2f, 3.1415926f});
mat.data[2][0] *= 100000.4f;
const guf_mat4x4 mat_cpy = mat;
printf("Matrix:\n");
guf_mat4x4_print_with_precision(&mat, stdout, 8);
guf_mat4x4 mat_inv;
bool invertible = guf_mat4x4_inverted(&mat, &mat_inv);
if (!invertible) {
printf("Not invertible\n");
} else {
printf("Inverse:\n");
guf_mat4x4_print_with_precision(&mat_inv, stdout, 8);
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&mat_inv, &mat));
GUF_ASSERT_RELEASE(guf_mat4x4_nearly_equal(&mat, &mat_cpy, 1e-4f, 1e-5f));
}
mat = (guf_mat4x4) {.data = {
{1, 1.3f, 1, 1},
{2, 2.6f, 2, 2},
{0, 0, 2, 4},
{0, 0, 0, 1}
}};
printf("Matrix 2:\n");
guf_mat4x4_print_with_precision(&mat, stdout, 8);
invertible = guf_mat4x4_inverted(&mat, &mat_inv);
if (!invertible) {
printf("Not invertible\n");
} else {
printf("Inverse:\n");
guf_mat4x4_print_with_precision(&mat_inv, stdout, 8);
}
bool leak = false;
if (guf_alloc_tracker_found_leak(&allocator_ctx.tracker)) {
printf("Found memory leak:\n");
guf_alloc_tracker_print(&allocator_ctx.tracker, stderr);
leak = true;
}
if (guf_alloc_tracker_found_leak(&zero_init_allocator_ctx.tracker)) {
printf("Found memory leak:\n");
guf_alloc_tracker_print(&zero_init_allocator_ctx.tracker, stderr);
leak = true;
}
return leak ? EXIT_FAILURE: EXIT_SUCCESS;
}

View File

@ -1,2 +0,0 @@
#define GUF_ALLOC_LIBC_IMPL
#include "guf_alloc_libc.h"

View File

@ -1,2 +0,0 @@
#define GUF_ALLOC_TRACKER_IMPL
#include "guf_alloc_tracker.h"

View File

@ -1,2 +0,0 @@
#define GUF_MATH_CKDINT_IMPL
#include "guf_math_ckdint.h"

View File

@ -1,55 +0,0 @@
#include "dbuf_impl.h"
#define GUF_DBUF_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_i32
#define GUF_T int32_t
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_char
#define GUF_T char
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_DBUF_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_DBUF_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_str_view
#define GUF_DBUF_NAME dbuf_str_view
#define GUF_T_EQ guf_str_view_equal
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_str
#define GUF_DBUF_NAME dbuf_str
#define GUF_T_COPY guf_str_copy
#define GUF_T_MOVE guf_str_move
#define GUF_T_FREE guf_str_free
#define GUF_T_EQ guf_str_equal
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"

View File

@ -1,53 +0,0 @@
#ifndef GUF_DBUF_IMPL_H
#define GUF_DBUF_IMPL_H
#include "guf_cstr.h"
#include "guf_str.h"
#define GUF_DBUF_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_i32
#define GUF_T int32_t
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_char
#define GUF_T char
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_DBUF_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_DBUF_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#include "guf_dbuf.h"
#define GUF_T guf_str_view
#define GUF_DBUF_NAME dbuf_str_view
#define GUF_T_EQ guf_str_view_equal
#include "guf_dbuf.h"
#define GUF_T guf_str
#define GUF_DBUF_NAME dbuf_str
#define GUF_T_COPY guf_str_copy
#define GUF_T_MOVE guf_str_move
#define GUF_T_FREE guf_str_free
#define GUF_T_EQ guf_str_equal
#include "guf_dbuf.h"
#endif

View File

@ -1,47 +0,0 @@
#include "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_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_cstr_int
#define GUF_DICT_IMPL
#include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str_view
#define GUF_DICT_KEY_HASH guf_str_view_hash
#define GUF_DICT_KEY_T_EQ guf_str_view_equal
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_sv_i32
#define GUF_DICT_IMPL
// #define GUF_DICT_64_BIT_IDX
// #define GUF_DICT_PROBE_LINEAR
// #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str
#define GUF_DICT_KEY_HASH guf_str_hash
#define GUF_DICT_KEY_T_EQ guf_str_equal
#define GUF_DICT_KEY_T_CMP guf_str_cmp
#define GUF_DICT_KEY_T_COPY guf_str_copy
#define GUF_DICT_KEY_T_MOVE guf_str_move
#define GUF_DICT_KEY_T_FREE guf_str_free
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_str_i32
#define GUF_DICT_IMPL
// #define GUF_DICT_64_BIT_IDX
// #define GUF_DICT_PROBE_LINEAR
// #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h"
#define GUF_DICT_KEY_T int32_t
#define GUF_DICT_KEY_HASH int32_hash
#define GUF_DICT_KEY_T_EQ int32_eq
#define GUF_DICT_VAL_T bool
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_i32_bool
#define GUF_DICT_IMPL
#include "guf_dict.h"

View File

@ -1,61 +0,0 @@
#ifndef GUF_DICT_IMPL_H
#define GUF_DICT_IMPL_H
#include "guf_common.h"
#include "guf_cstr.h"
#include "guf_str.h"
#include "guf_hash.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_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_cstr_int
#include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str_view
#define GUF_DICT_KEY_HASH guf_str_view_hash
#define GUF_DICT_KEY_T_EQ guf_str_view_equal
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_sv_i32
// #define GUF_DICT_64_BIT_IDX
// #define GUF_DICT_PROBE_LINEAR
// #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str
#define GUF_DICT_KEY_HASH guf_str_hash
#define GUF_DICT_KEY_T_EQ guf_str_equal
#define GUF_DICT_KEY_T_CMP guf_str_cmp
#define GUF_DICT_KEY_T_COPY guf_str_copy
#define GUF_DICT_KEY_T_MOVE guf_str_move
#define GUF_DICT_KEY_T_FREE guf_str_free
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_str_i32
// #define GUF_DICT_64_BIT_IDX
// #define GUF_DICT_PROBE_LINEAR
// #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h"
static inline guf_hash_size_t int32_hash(const int32_t *a)
{
return guf_hash(a, sizeof(int32_t), GUF_HASH_INIT); // TODO: byte order...
}
static inline bool int32_eq(const int32_t *a, const int32_t *b)
{
return *a == *b;
}
#define GUF_DICT_KEY_T int32_t
#define GUF_DICT_KEY_HASH int32_hash
#define GUF_DICT_KEY_T_EQ int32_eq
#define GUF_DICT_VAL_T bool
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_i32_bool
#include "guf_dict.h"
#endif

View File

@ -1 +0,0 @@
#include "guf_init.h"

View File

@ -1,2 +0,0 @@
#define GUF_LINALG_IMPL
#include "guf_linalg.h"

View File

@ -1,2 +0,0 @@
#define GUF_RAND_IMPL
#include "guf_rand.h"

View File

@ -1,17 +0,0 @@
#include "sort_impl.h"
#define GUF_T float
#define GUF_SORT_IMPL
#include "guf_sort.h"
#define GUF_T int32_t
#define GUF_SORT_IMPL
#include "guf_sort.h"
#define GUF_T int8_t
#define GUF_SORT_IMPL
#include "guf_sort.h"
#define GUF_T guf_cstr_heap
#define GUF_SORT_IMPL
#include "guf_sort.h"

View File

@ -1,18 +0,0 @@
#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

View File

@ -1,2 +0,0 @@
#define GUF_STR_IMPL
#include "guf_str.h"

View File

@ -1,64 +0,0 @@
#include <vector>
#include <string>
#include <cstdio>
#include <iostream>
#include "test_dbuf.hpp"
#include "test_dict.hpp"
#include "test_utf8.hpp"
#include "test_str.hpp"
#include "test_ckdint.hpp"
extern "C"
{
#include "guf_assert.h"
#include "guf_math.h"
}
static std::vector<std::unique_ptr<Test>> g_tests {};
static void init_tests()
{
g_tests.push_back(std::make_unique<DbufIntTest>("DbufIntTest"));
g_tests.push_back(std::make_unique<DbufCstringTest>("DbufCstringTest"));
g_tests.push_back(std::make_unique<DbufStrTest>("DbufStrTest"));
g_tests.push_back(std::make_unique<DictSvToIntTest>("DictSvToIntTest"));
g_tests.push_back(std::make_unique<UTF8Test>("UTF8Test"));
g_tests.push_back(std::make_unique<StrTest>("StrTest"));
g_tests.push_back(std::make_unique<CkdIntTest>("CkdIntTest"));
}
int main()
{
init_tests();
std::cout << "Running " << g_tests.size() << " tests...\n";
// std::cout << "max cap 1:" << dict_sv_i32_max_capacity() << "\n";
// std::cout << "max cap 2:" << dict_cstr_int_max_capacity() << "\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;
}

View File

@ -1,108 +0,0 @@
#ifndef GUF_TEST_HPP
#define GUF_TEST_HPP
#include <stack>
#include <string>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <iomanip>
extern "C" {
#include "guf_common.h"
#include "guf_assert.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& nm) : name{nm} {}
virtual ~Test() = default;
size_t total_checks() const
{
return num_passed_checks + num_failed_checks;
}
virtual void run() = 0;
void before_run()
{
time_start = std::chrono::steady_clock::now();
}
void after_run()
{
done = true;
passed = (num_failed_checks == 0);
time_end = std::chrono::steady_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

View File

@ -1,361 +0,0 @@
#include "test_ckdint.hpp"
extern "C"
{
#include "guf_math_ckdint.h"
}
/*
CkdIntTest:
*/
void CkdIntTest::run()
{
push_check_name("test_ckd");
test_ckd();
pop_check_name();
push_check_name("test_ckd_uint");
test_ckd_uint();
pop_check_name();
}
void CkdIntTest::test_ckd()
{
for (int32_t a = INT8_MIN; a <= INT8_MAX; ++a) {
for (int32_t b = INT8_MIN; b <= INT8_MAX; ++b) {
const int32_t add_res = a + b;
const guf_math_ckd_result ckd_add = guf_ckd_add_i8((int8_t)a, (int8_t)b);
TEST_CHECK(ckd_add == guf_ckd_add_i8((int8_t)b, (int8_t)a));
TEST_CHECK(ckd_add == guf_ckd_add_least_i8((int_least8_t)a, (int_least8_t)b));
if (add_res > INT8_MAX) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == INT8_MAX);
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MIN + (add_res % (INT8_MAX + 1)));
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(wrapped == wrapped2);
}
else if (add_res < INT8_MIN) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_NEG);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == INT8_MIN);
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MAX - (-add_res % (-INT8_MIN + 1)));
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(wrapped == wrapped2);
}
else {
TEST_CHECK(ckd_add == GUF_MATH_CKD_SUCCESS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == add_res);
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == add_res);
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(wrapped == wrapped2);
}
const int32_t sub_res = a - b;
const guf_math_ckd_result ckd_sub = guf_ckd_sub_i8((int8_t)a, (int8_t)b);
TEST_CHECK(ckd_sub == guf_ckd_sub_least_i8((int_least8_t)a, (int_least8_t)b));
if (sub_res > INT8_MAX) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated;
TEST_CHECK(guf_saturating_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == INT8_MAX);
int8_t wrapped;
TEST_CHECK(guf_wrapping_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MIN + (sub_res % (INT8_MAX + 1)));
} else if (sub_res < INT8_MIN) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_NEG);
int8_t saturated;
TEST_CHECK(guf_saturating_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == INT8_MIN);
int8_t wrapped;
TEST_CHECK(guf_wrapping_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MAX - (-sub_res % (-INT8_MIN + 1)));
} else {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_SUCCESS);
int8_t saturated;
TEST_CHECK(guf_saturating_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == sub_res);
int8_t wrapped;
TEST_CHECK(guf_wrapping_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == sub_res);
}
const int32_t mul_res = a * b;
const guf_math_ckd_result ckd_mul = guf_ckd_mul_i8((int8_t)a, (int8_t)b);
TEST_CHECK(ckd_mul == guf_ckd_mul_least_i8((int_least8_t)a, (int_least8_t)b));
TEST_CHECK(ckd_mul == guf_ckd_mul_i8((int8_t)b, (int8_t)a));
if (mul_res > INT8_MAX) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == INT8_MAX);
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(wrapped == wrapped2);
// TODO: check wrapped
} else if (mul_res < INT8_MIN) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_NEG);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == INT8_MIN);
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(wrapped == wrapped2);
// TODO: check wrapped
} else {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_SUCCESS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == mul_res);
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == mul_res);
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(wrapped == wrapped2);
}
}
}
int8_t mul_i8_res = -1;
TEST_CHECK(guf_wrapping_mul_i8(42, 5, &mul_i8_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i8_res == -46);
mul_i8_res = -1;
TEST_CHECK(guf_wrapping_mul_i8(5, 42, &mul_i8_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i8_res == -46);
int16_t mul_i16_res = -1245;
TEST_CHECK(guf_wrapping_mul_i16(32767, 2, &mul_i16_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i16_res == -2);
mul_i16_res = -1245;
TEST_CHECK(guf_wrapping_mul_i16(-32767, 2, &mul_i16_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i16_res == 2);
/*
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2024
use std::num::Wrapping;
fn main() {
let a = Wrapping(-314159265_i32);
let b = Wrapping(4096_i32);
println!("{}", a * b);
}
*/
int32_t mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(INT32_MAX, 2, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -2);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(2, INT32_MAX, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -2);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(INT32_MAX, -2, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32_res == 2);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(-2, INT32_MAX, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32_res == 2);
TEST_CHECK(guf_wrapping_mul_i32(42002718, 314159265, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -972735522);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(314159265, 42002718, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -972735522);
mul_i32_res = 12345;
guf_wrapping_mul_i32(42002718, 314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == -972735522);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-42002718, 314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == 972735522);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-88888888, 99999999, &mul_i32_res);
TEST_CHECK(mul_i32_res == 1374494264);
mul_i32_res = 12345;
guf_wrapping_mul_i32(INT32_MIN, -1, &mul_i32_res);
TEST_CHECK(mul_i32_res == INT32_MIN);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-2147483648, 2147483640, &mul_i32_res);
TEST_CHECK(mul_i32_res == 0);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-2048, -314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == -846919680);
mul_i32_res = 12345;
guf_wrapping_mul_i32(4096, -314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == 1693839360);
int_least32_t mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(INT32_MAX, 2, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -2);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(2, INT32_MAX, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -2);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(INT32_MAX, -2, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32least_res == 2);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(-2, INT32_MAX, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32least_res == 2);
TEST_CHECK(guf_wrapping_mul_least_i32(42002718, 314159265, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -972735522);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(314159265, 42002718, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -972735522);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(42002718, 314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == -972735522);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-42002718, 314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 972735522);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-88888888, 99999999, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 1374494264);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(INT32_MIN, -1, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == INT32_MIN);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-2147483648, 2147483640, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 0);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-2048, -314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == -846919680);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(4096, -314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 1693839360);
ptrdiff_t ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MAX, 1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(ptrdiff_res == PTRDIFF_MAX);
ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MIN, -1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(ptrdiff_res == PTRDIFF_MIN);
ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_mul_ptrdiff_t(PTRDIFF_MAX, 2, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(ptrdiff_res == PTRDIFF_MAX);
ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_mul_ptrdiff_t(PTRDIFF_MIN, 2, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(ptrdiff_res == PTRDIFF_MIN);
}
void CkdIntTest::test_ckd_uint()
{
for (int32_t a = 0; a <= UINT8_MAX; ++a) {
for (int32_t b = 0; b <= UINT8_MAX; ++b) {
const int32_t add_res = a + b;
const guf_math_ckd_result ckd_add = guf_ckd_add_u8((uint8_t)a, (uint8_t)b);
GUF_ASSERT(ckd_add == guf_ckd_add_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (add_res > UINT8_MAX) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS);
uint8_t saturated;
TEST_CHECK(guf_saturating_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == UINT8_MAX);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(static_cast<int32_t>(wrapped) == 0 + (add_res % (UINT8_MAX + 1)));
}
else {
TEST_CHECK(ckd_add == GUF_MATH_CKD_SUCCESS);
uint8_t saturated;
TEST_CHECK(guf_saturating_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == add_res);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == add_res);
}
const int32_t sub_res = a - b;
const guf_math_ckd_result ckd_sub = guf_ckd_sub_u8((uint8_t)a, (uint8_t)b);
GUF_ASSERT(ckd_sub == guf_ckd_sub_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (sub_res < 0) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_NEG);
uint8_t saturated;
TEST_CHECK(guf_saturating_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == 0);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(wrapped == static_cast<uint8_t>(static_cast<uint8_t>(a) - static_cast<uint8_t>(b)));
} else {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_SUCCESS);
uint8_t saturated;
TEST_CHECK(guf_saturating_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == sub_res);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == sub_res);
}
const int32_t mul_res = a * b;
const guf_math_ckd_result ckd_mul = guf_ckd_mul_u8((uint8_t)a, (uint8_t)b);
GUF_ASSERT(ckd_mul == guf_ckd_mul_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (mul_res > UINT8_MAX) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS);
uint8_t saturated;
TEST_CHECK(guf_saturating_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == UINT8_MAX);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(wrapped == static_cast<uint8_t>(static_cast<uint8_t>(a) * static_cast<uint8_t>(b)));
} else {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_SUCCESS);
uint8_t saturated;
TEST_CHECK(guf_saturating_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == mul_res);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == mul_res);
}
}
}
}

View File

@ -1,12 +0,0 @@
#pragma once
#include "test.hpp"
struct CkdIntTest : public Test
{
CkdIntTest(const std::string& nm) : Test(nm) {};
void run() override;
private:
void test_ckd();
void test_ckd_uint();
};

View File

@ -1,685 +0,0 @@
#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));
}

View File

@ -1,69 +0,0 @@
#pragma once
#include <vector>
#include "test.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
#include "impls/dbuf_impl.h"
}
struct DbufIntTest : public Test
{
DbufIntTest(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 1, "DbufIntTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
}
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
std::vector<int> dbuf_to_vec(dbuf_int *dbuf);
void test_push(dbuf_int *dbuf, int n);
void test_insert_remove(int n);
};
struct DbufCstringTest : public Test
{
DbufCstringTest(std::string nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 2, "DbufCstringTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
}
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
void test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step = 1);
void test_push_insert_erase(int n, ptrdiff_t start_cap = 0);
void test_find(int n = 32);
};
struct DbufStrTest : public Test
{
DbufStrTest(std::string nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DbufStrTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
}
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
void test_push_insert_erase(size_t n, ptrdiff_t start_cap = 0);
};

View File

@ -1,431 +0,0 @@
#include "test_dict.hpp"
#include <unordered_map>
#include <cstring>
extern "C"
{
#include "guf_alloc_libc.h"
#include "guf_str.h"
#include "impls/dict_impl.h"
#include "impls/dbuf_impl.h"
}
/*
DictSvToIntTest:
*/
void DictSvToIntTest::run()
{
if (done) {
return;
}
push_check_name("insert_lookup(\"utf8-test.txt\")");
if (TEST_CHECK(load_file(TEST_DATA_DIR "/utf8-test.txt"))) {
insert_lookup();
for (ptrdiff_t i = 0; i <= 64; ++i) {
insert_lookup(i);
}
insert_lookup(512);
insert_lookup(1997);
insert_lookup(1999);
}
free_file();
pop_check_name();
push_check_name("insert_lookup(\"bartleby.txt\")");
if (TEST_CHECK(load_file(TEST_DATA_DIR "/bartleby.txt"))) {
insert_lookup();
insert_lookup(201);
}
free_file();
pop_check_name();
//guf_alloc_tracker_print(&allocator_ctx.tracker, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
}
void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
{
std::unordered_map<std::string_view, int32_t> word_cnt_map {};
dict_sv_i32 word_cnt_dict {};
dict_str_i32 word_cnt_dict_str {};
if (inital_dict_cap) {
dict_sv_i32_init_with_capacity(&word_cnt_dict, &allocator, inital_dict_cap.value());
dict_str_i32_init_with_capacity(&word_cnt_dict_str, &allocator, inital_dict_cap.value());
} else {
dict_sv_i32_init(&word_cnt_dict, &allocator);
dict_str_i32_init(&word_cnt_dict_str, &allocator);
}
dbuf_str_view delims = dbuf_str_view_new(&allocator);
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_WHITESPACE); ++i) {
guf_str_view d = {.str = GUF_UTF8_WHITESPACE[i], .len = (ptrdiff_t)strlen(GUF_UTF8_WHITESPACE[i])};
dbuf_str_view_push_val(&delims, d);
}
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_COMMON_PUNCT); ++i) {
guf_str_view d = {.str = GUF_UTF8_COMMON_PUNCT[i], .len = (ptrdiff_t)strlen(GUF_UTF8_COMMON_PUNCT[i])};
dbuf_str_view_push_val(&delims, d);
}
guf_str_tok_state tok_state = guf_str_tok_state_new(guf_str_view{.str = text_buf.data, .len = text_buf.size}, delims.data, delims.size, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
while (guf_str_tok_next(&tok_state, true)) {
guf_str_view tok = tok_state.cur_tok;
// if (tok.len <= 0) {
// continue;
// }
std::string_view sv(tok.str , tok.len);
//std::cout << sv << std::string_view(tok_state.cur_delim.str, tok_state.cur_delim.len);
TEST_CHECK(dict_sv_i32_contains(&word_cnt_dict, &tok) == word_cnt_map.contains(sv));
if (!dict_sv_i32_contains(&word_cnt_dict, &tok)) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, tok, 1, GUF_CPY_VALUE, GUF_CPY_VALUE);
word_cnt_map.insert({sv, 1});
if (TEST_CHECK(!dict_str_i32_contains_val_arg(&word_cnt_dict_str, guf_str_new_readonly(tok)))) {
dict_str_i32_insert_val_arg(&word_cnt_dict_str, guf_str_new(tok, &allocator), 1, GUF_CPY_MOVE, GUF_CPY_VALUE);
}
} else {
int32_t *cnt = dict_sv_i32_at_val_arg(&word_cnt_dict, tok);
if (TEST_CHECK(cnt)) {
*cnt += 1;
}
int32_t *cnt_2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(tok));
if (TEST_CHECK(cnt_2)) {
*cnt_2 += 1;
}
// else {
// std::cout << "tok: " << std::string_view{tok.str, (size_t)tok.len} << "\n";
// }
word_cnt_map.at(sv) += 1;
}
// printf("tok_len: %td ", tok.len);
// printf("'%.*s'\n", (int)tok.len, tok.str);
TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict));
TEST_CHECK(dict_str_i32_debug_valid_size(&word_cnt_dict_str));
}
dbuf_str_view_free(&delims, NULL);
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == std::ssize(word_cnt_map));
TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict));
TEST_CHECK(dict_str_i32_size(&word_cnt_dict_str) == std::ssize(word_cnt_map));
TEST_CHECK(dict_str_i32_debug_valid_size(&word_cnt_dict_str));
for (const auto & [word, cnt] : word_cnt_map ) {
guf_str_view sv = {.str = word.data(), .len = (ptrdiff_t)word.size()};
int32_t *res = dict_sv_i32_at(&word_cnt_dict, &sv);
int32_t *res2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(sv));
TEST_CHECK(res && *res == cnt);
TEST_CHECK(res2 && *res2 == cnt);
}
ptrdiff_t i = 0;
GUF_CNT_FOREACH(&word_cnt_dict, dict_sv_i32, kv_it) {
const dict_sv_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val;
const std::string_view sv(kv->key.str, kv->key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt);
}
}
++i;
}
TEST_CHECK(i == dict_sv_i32_size(&word_cnt_dict));
TEST_CHECK(i == std::ssize(word_cnt_map));
TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict));
i = 0;
GUF_CNT_FOREACH(&word_cnt_dict_str, dict_str_i32, kv_it) {
const dict_str_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val;
const std::string_view sv(guf_str_const_cstr(&kv->key), guf_str_len(&kv->key));
// std::cout << sv << "\n";
if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt);
}
}
++i;
}
TEST_CHECK(i == dict_str_i32_size(&word_cnt_dict_str));
TEST_CHECK(i == std::ssize(word_cnt_map));
TEST_CHECK(dict_str_i32_debug_valid_size(&word_cnt_dict_str));
// std::cout << "load fac: " << dict_sv_i32_load_factor(&word_cnt_dict) << ", cap: " << word_cnt_dict.kv_indices_cap << " elem cap: " << word_cnt_dict.kv_elems.capacity << "\n";
// std::cout << "size: " << dict_sv_i32_size(&word_cnt_dict) << ", max probelen: " << word_cnt_dict.max_probelen << "\n";
// std::cout << "mem usage: " << dict_sv_i32_memory_usage(&word_cnt_dict) << "\n";
// Erase tests:
const double load_fac_before_erase = dict_sv_i32_load_factor(&word_cnt_dict);
const ptrdiff_t size_before_erase = dict_sv_i32_size(&word_cnt_dict);
ptrdiff_t num_del = 0;
while (dict_sv_i32_size(&word_cnt_dict) > size_before_erase / 2) {
dict_sv_i32_kv *kv = NULL;
if (num_del % 2) {
dict_sv_i32_iter it = dict_sv_i32_begin(&word_cnt_dict);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, it));
kv = it.ptr;
} else {
dict_sv_i32_iter rit = dict_sv_i32_rbegin(&word_cnt_dict);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, rit));
kv = rit.ptr;
}
GUF_ASSERT_RELEASE(kv);
const guf_str_view key = kv->key;
const bool del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
std::string_view sv(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
TEST_CHECK(!word_cnt_map.contains(sv));
if (del_success) {
++num_del;
}
}
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) >= 0);
TEST_CHECK(size_before_erase - num_del == dict_sv_i32_size(&word_cnt_dict));
TEST_CHECK(std::ssize(word_cnt_map) == dict_sv_i32_size(&word_cnt_dict));
if (dict_sv_i32_size(&word_cnt_dict) != 0) {
TEST_CHECK(load_fac_before_erase == dict_sv_i32_load_factor(&word_cnt_dict));
} else {
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
}
if (dict_sv_i32_size(&word_cnt_dict) >= 4) {
dict_sv_i32_kv_dbuf_iter it = dict_sv_i32_begin(&word_cnt_dict);
it = dict_sv_i32_iter_next(&word_cnt_dict, it, 1);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, it));
guf_str_view key = it.ptr->key;
bool del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
std::string_view sv(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
it = dict_sv_i32_rbegin(&word_cnt_dict);
it = dict_sv_i32_iter_next(&word_cnt_dict, it, 1);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, it));
key = it.ptr->key;
del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
sv = std::string_view(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
}
TEST_CHECK(std::ssize(word_cnt_map) == dict_sv_i32_size(&word_cnt_dict));
i = 0;
GUF_CNT_FOREACH(&word_cnt_dict, dict_sv_i32, kv_it) {
const dict_sv_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val;
const std::string_view sv(kv->key.str, (size_t)kv->key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt);
}
++i;
}
}
TEST_CHECK(i == word_cnt_dict.kv_elems.size);
TEST_CHECK(i == std::ssize(word_cnt_map));
while (dict_sv_i32_size(&word_cnt_dict) > 0) {
const dict_sv_i32_iter beg = dict_sv_i32_begin(&word_cnt_dict);
if (TEST_CHECK(!dict_sv_i32_iter_is_end(&word_cnt_dict, beg))) {
const guf_str_view key = beg.ptr->key;
if (TEST_CHECK(dict_sv_i32_contains(&word_cnt_dict, &key))) {
const bool del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
}
const std::string_view sv(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
}
}
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 0 && word_cnt_map.size() == 0);
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej"), (size_t)64, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!"), (size_t)128, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum"), (size_t)256, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med"), (size_t)512, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi."), (size_t)1024, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 5);
int32_t *val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej"));
TEST_CHECK(val && *val == 64);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum"));
TEST_CHECK(val && *val == 256);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi."));
TEST_CHECK(val && *val == 1024);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!"));
TEST_CHECK(val && *val == 128);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med"));
TEST_CHECK(val && *val == 512);
TEST_CHECK(word_cnt_dict.kv_elems.size == 5);
TEST_CHECK(word_cnt_dict.kv_elems.data[0].val == 64 && std::strcmp(word_cnt_dict.kv_elems.data[0].key.str, "Hej") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[1].val == 128 && std::strcmp(word_cnt_dict.kv_elems.data[1].key.str, "verden!") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[2].val == 256 && std::strcmp(word_cnt_dict.kv_elems.data[2].key.str, "Flødeskum") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[3].val == 512 && std::strcmp(word_cnt_dict.kv_elems.data[3].key.str, "med") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[4].val == 1024 && std::strcmp(word_cnt_dict.kv_elems.data[4].key.str, "Faxe Kondi.") == 0);
const double load_fac_beg = dict_sv_i32_load_factor(&word_cnt_dict);
const ptrdiff_t cap_begin = word_cnt_dict.kv_indices_cap;
ptrdiff_t del = 0;
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < cap_begin + 128; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej"), 64, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi.")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < 256; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi."), 128, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi.")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < 512 + cap_begin; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med"), 256, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < 71; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum"), 512, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!")));
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
for (ptrdiff_t n = 0; n < 201; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!"), 128, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) > 0);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!")));
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(word_cnt_dict.kv_elems.size == 0);
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 0);
std::string str;
for (size_t c = 0; c < GUF_STR_SSO_BUF_CAP * 4; ++c) {
str += c % 2 ? "AAA" : "aaa";
}
guf_str str_cpy = guf_str_new(guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}, &allocator);
dict_str_i32_insert_val_arg(&word_cnt_dict_str, str_cpy, 42, GUF_CPY_DEEP, GUF_CPY_VALUE);
int32_t *foo = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(guf_str_view_from_str(&str_cpy)));
if (TEST_CHECK(foo)) {
TEST_CHECK(*foo == 42);
}
guf_str_append(&str_cpy, GUF_CSTR_LIT_TO_VIEW_CPP("Foobar"));
int32_t *foo2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}));
if (TEST_CHECK(foo2)) {
TEST_CHECK(*foo2 == 42);
}
guf_str_free(&str_cpy, NULL);
dict_sv_i32_free(&word_cnt_dict, NULL);
dict_str_i32_free(&word_cnt_dict_str, NULL);
bool dbuf_null = !word_cnt_dict.kv_elems.data && !word_cnt_dict.kv_elems.allocator && !word_cnt_dict.kv_elems.capacity && !word_cnt_dict.kv_elems.size;
TEST_CHECK(dbuf_null && !word_cnt_dict.kv_indices && !word_cnt_dict.kv_indices_cap && !word_cnt_dict.max_probelen && !word_cnt_dict.num_tombstones);
}
bool DictSvToIntTest::load_file(const char *fname)
{
FILE *in_file {nullptr};
if (!in_file) {
in_file = fopen(fname, "r");
}
GUF_ASSERT_RELEASE(in_file);
dbuf_char_init(&text_buf, 128, &allocator);
int c = EOF;
while ((c = fgetc(in_file)) != EOF) {
dbuf_char_push_val(&text_buf, (char)c);
text_vec.push_back((char)c);
}
fclose(in_file);
// dbuf_char_insert_val(&text_buf, '\xC0', 1);
// text_vec.insert(text_vec.cbegin() + 1, '\xC0');
return TEST_CHECK(std::ssize(text_vec) == text_buf.size);
}
void DictSvToIntTest::free_file()
{
dbuf_char_free(&text_buf, NULL);
text_buf = {};
text_vec.clear();
}

View File

@ -1,35 +0,0 @@
#pragma once
#include <vector>
#include <optional>
#include <string>
#include "test.hpp"
extern "C"
{
#include "impls/dbuf_impl.h"
#include "guf_alloc_libc.h"
}
struct DictSvToIntTest : public Test
{
DictSvToIntTest(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DictSvToIntTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
};
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
dbuf_char text_buf {};
std::vector<char> text_vec {};
bool load_file(const char *fname);
void free_file();
void insert_lookup(std::optional<ptrdiff_t> inital_dict_cap = {});
};

View File

@ -1,379 +0,0 @@
#include "test_str.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
}
/*
StrTest:
*/
void StrTest::run()
{
if (done) {
return;
}
const std::vector<std::string> words = {
"",
"\0",
"Hello",
"Othell\0o",
"f\0\0",
"\0",
"0",
"a",
"ab",
"🌈 waow a rainboge!",
"orange cat(1) :3",
"xes yag",
"Hello, world! This is a pretty darn long string I'd say...",
"I want to eat crayons. I crave crayons because they are tasty, and everybody telling me crayons are not edible must be either lying or dumb. I like trains. 42 is a number. 3.14159265... is not a rational number, and it is called pi. I ate some pie (it was a crayon pie).",
std::string(32, 'a'),
std::string(64, 'b'),
std::string(1024, 'a'),
std::string(2048, 'a'),
std::string(4096, 'a'),
std::string(5001, 'a'),
std::string(7121, 'a'),
std::string(2000, 'a'),
std::string(GUF_STR_SSO_BUF_CAP, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 1, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 1, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 4, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 5, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 6, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 7, 'a'),
};
push_check_name("init_empy");
test_init_empty();
pop_check_name();
push_check_name("append_char");
for (const auto& word : words) {
test_init_free(word);
test_append_char(word);
test_append_char(word, true);
}
pop_check_name();
push_check_name("append_str");
for (size_t i = 0; i < words.size(); ++i) {
const auto& w1 = words.at(i);
append_str(w1, w1);
append_str(w1, w1);
for (size_t j = i + 1; j < words.size(); ++j) {
const auto& w2 = words.at(j);
append_str(w1, w2);
append_str(w2, w1);
}
}
pop_check_name();
push_check_name("test_popsplit");
std::vector<std::string_view> split = test_popsplit("1997-04-01", "-");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "1997" && split.at(1) == "04" && split.at(2) == "01");
}
split = test_popsplit("1997-04-01-", "-");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "1997" && split.at(1) == "04" && split.at(2) == "01");
}
split = test_popsplit("2025/05/08", "/");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "2025" && split.at(1) == "05" && split.at(2) == "08");
}
split = test_popsplit("2025/05/08/", "/");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "2025" && split.at(1) == "05" && split.at(2) == "08");
}
split = test_popsplit("2025/05/08//", "/");
if (TEST_CHECK(split.size() == 4)) {
TEST_CHECK(split.at(0) == "2025" && split.at(1) == "05" && split.at(2) == "08" && split.at(3) == "");
}
split = test_popsplit("/2025/05/08", "/");
if (TEST_CHECK(split.size() == 4)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == "2025" && split.at(2) == "05" && split.at(3) == "08");
}
split = test_popsplit("//2025/05/08", "/");
if (TEST_CHECK(split.size() == 5)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == "" && split.at(2) == "2025" && split.at(3) == "05" && split.at(4) == "08");
}
split = test_popsplit("I eat formidable crayons, oof, for real", "foo");
if (TEST_CHECK(split.size() == 1)) {
TEST_CHECK(split.at(0) == "I eat formidable crayons, oof, for real");
}
split = test_popsplit("Hej <<", "<<");
if (TEST_CHECK(split.size() == 1)) {
TEST_CHECK(split.at(0) == "Hej ");
}
split = test_popsplit("Hej << verden", "<<");
if (TEST_CHECK(split.size() == 2)) {
TEST_CHECK(split.at(0) == "Hej " && split.at(1) == " verden");
}
split = test_popsplit("<< Hej << verden", "<<");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == " Hej " && split.at(2) == " verden");
}
split = test_popsplit("<< Hej << verden <<< foo<>", "<<");
if (TEST_CHECK(split.size() == 4)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == " Hej " && split.at(2) == " verden " && split.at(3) == "< foo<>");
}
split = test_popsplit("I eat tofu", "");
if (TEST_CHECK(split.size() == 1)) {
TEST_CHECK(split.at(0) == "I eat tofu");
}
split = test_popsplit("At 3 a.m. during FULL-moon FULL-STOP Next to the public-library's -STOP sign FULL-STOP", "FULL-STOP");
if (TEST_CHECK(split.size() == 2)) {
TEST_CHECK(split.at(0) == "At 3 a.m. during FULL-moon " && split.at(1) == " Next to the public-library's -STOP sign ");
}
split = test_popsplit("At 3 a.m. during FULL-moon FULL-STOP Next to the public-library's -STOP sign FULL-STOPI like trains, FULL-STO", "FULL-STOP");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "At 3 a.m. during FULL-moon " && split.at(1) == " Next to the public-library's -STOP sign " && split.at(2) == "I like trains, FULL-STO");
}
split = test_popsplit("At 3 a.m. during FULL-moon FULL-STOP Next to the public-library's -STOP sign FULL-STOPI like trains, FULL-STO Poo", "FULL-STOP");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "At 3 a.m. during FULL-moon " && split.at(1) == " Next to the public-library's -STOP sign " && split.at(2) == "I like trains, FULL-STO Poo");
}
pop_check_name();
push_check_name("get_toks");
std::vector<std::string_view> tok_words = {"hello", "world", "cat", "vertex", "normal", "pizza", "running", "mouse", "playing", "adjacent"};
std::vector<std::string_view> delims = {",", " ", "\n", "\t", "\r"};
for (int is_trailing = 0; is_trailing < 2; ++is_trailing) {
for (ptrdiff_t num_words = 1; num_words < std::ssize(tok_words); ++num_words) {
std::string str = "";
for (ptrdiff_t j = 0; j < num_words; ++j) {
str += tok_words.at(j);
if (j < num_words - 1 || is_trailing) {
str += ", ";
}
}
std::vector<std::string_view> toks = get_toks(std::string_view{str}, delims, false, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
if (TEST_CHECK(std::ssize(toks) == num_words)) {
for (ptrdiff_t i = 0; i < num_words; ++i) {
TEST_CHECK(toks.at(i) == tok_words.at(i));
}
}
}
}
std::string_view tok_str = "<stats>age: 28, occupation: NULL, crayons_eaten: 256 </stats>";
delims = {"<stats>", "</stats>", ":", ",", " ", "\t", "<stats", "<", ">", "</"};
auto tok_result = get_toks(tok_str, delims, true, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
TEST_CHECK(tok_result.size() == 19);
TEST_CHECK(tok_result.at(18) == "</stats>" && tok_result.at(0) == "<stats>" && tok_result.at(1) == "age" && tok_result.at(2) == ":" && tok_result.at(3) == " " && tok_result.at(4) == "28");
tok_result = get_toks(tok_str, delims, false, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
TEST_CHECK(tok_result.size() == 6);
TEST_CHECK(tok_result.at(0) == "age" && tok_result.at(1) == "28" && tok_result.at(2) == "occupation" && tok_result.at(3) == "NULL" &&
tok_result.at(4) == "crayons_eaten" && tok_result.at(5) == "256");
pop_check_name();
// guf_alloc_tracker_print(&allocator_ctx.tracker, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
}
void StrTest::test_init_free(std::string str)
{
guf_str s0;
guf_str_init(&s0, GUF_CSTR_TO_VIEW_CPP(str.c_str()), &allocator);
guf_str s1 = guf_str_new(GUF_CSTR_TO_VIEW_CPP(str.c_str()), &allocator);
guf_str s2;
guf_str_init_from_cstr(&s2, str.c_str(), &allocator);
TEST_CHECK(guf_str_equal(&s0, &s1));
TEST_CHECK(guf_str_equal(&s0, &s2));
TEST_CHECK(guf_str_equal(&s1, &s2));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s0));
TEST_CHECK(str == guf_str_const_cstr(&s0));
TEST_CHECK(str == guf_str_cstr(&s0));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s1));
TEST_CHECK(str == guf_str_const_cstr(&s1));
TEST_CHECK(str == guf_str_cstr(&s1));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s2));
TEST_CHECK(str == guf_str_const_cstr(&s2));
TEST_CHECK(str == guf_str_cstr(&s2));
guf_str_free(&s0, NULL);
guf_str_free(&s1, NULL);
guf_str_free(&s2, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
TEST_CHECK(guf_str_is_uninit(&s1));
TEST_CHECK(guf_str_is_uninit(&s2));
}
void StrTest::test_init_empty()
{
std::string str = "";
guf_str s = GUF_STR_UNINITIALISED_CPP;
guf_str_init_empty(&s, &allocator);
TEST_CHECK(guf_str_len(&s) == 0);
TEST_CHECK(str == guf_str_const_cstr(&s));
guf_str_append_char(&s, 'a', 1024);
str.append(1024, 'a');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_str_append_char(&s, 'b', 24);
str.append(24, 'b');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_str_append_char(&s, 'c', 255);
str.append(255, 'c');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
*guf_str_at(&s, 0) = '<';
str.at(0) = '<';
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
*guf_str_at(&s, guf_str_len(&s) - 1) = '>';
str.at(str.size() - 1) = '>';
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_err err = GUF_ERR_NONE;
TEST_CHECK(NULL == guf_str_try_at(&s, guf_str_len(&s), &err));
TEST_CHECK(err != GUF_ERR_NONE && err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
TEST_CHECK(NULL == guf_str_try_at(&s, -1, &err));
TEST_CHECK(err != GUF_ERR_NONE && err == GUF_ERR_IDX_RANGE);
guf_str_free(&s, NULL);
TEST_CHECK(guf_str_is_uninit(&s));
}
void StrTest::test_append_char(std::string str, bool include_null)
{
guf_str s0 = guf_str_new(guf_str_view{.str = str.c_str(), .len = (ptrdiff_t)str.size()}, &allocator);
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s0));
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
for (int i = include_null ? 0 : 1; i < 128; ++i) {
char ch = (char)i;
guf_str_append_one_char(&s0, ch);
str.append(1, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
for (int i = include_null ? 0 : 1; i < 128; ++i) {
char ch = (char)i;
guf_str_append_char(&s0, ch, i);
str.append(i, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
guf_str_append_char(&s0, ch, i * 16);
str.append(i * 16, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
guf_str_free(&s0, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
}
void StrTest::append_str(const std::string& a, const std::string& b)
{
std::string str0 = a;
guf_str s0 = guf_str_new(guf_str_view{.str = str0.c_str(), .len = (ptrdiff_t)str0.size()}, &allocator);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str0.size());
TEST_CHECK((str0 == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
TEST_CHECK((str0 == std::string_view{guf_str_cstr(&s0), (size_t)guf_str_len(&s0)}));
for (int i = 0; i <= 64; ++i) {
str0.append(b);
guf_str_append(&s0, guf_str_view{.str = b.c_str(), .len = (ptrdiff_t)b.size()});
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str0.size());
TEST_CHECK((str0 == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
TEST_CHECK((str0 == std::string_view{guf_str_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
guf_str_free(&s0, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
}
std::vector<std::string_view> StrTest::test_popsplit(std::string_view str, std::string_view delim)
{
std::vector<std::string_view> result = {};
if (delim.size() > 0) { // NOTE: str.find with an empty delimiter returns 0, not std::string::npos
std::string_view src_cpp = str;
for (size_t idx = src_cpp.find(delim, 0); src_cpp.size() > 0; idx = src_cpp.find(delim, 0)) {
result.push_back(src_cpp.substr(0, idx));
if (idx == std::string::npos) {
break;
}
src_cpp = src_cpp.substr(idx + delim.size());
}
} else {
result.push_back(str);
}
const guf_str_view delim_sv = guf_str_view{.str = delim.data(), .len = (ptrdiff_t)delim.size()};
guf_str_view src = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
size_t n = 0;
do {
const guf_str_view popped = guf_str_view_pop_split(&src, delim_sv);
TEST_CHECK(n < result.size());
TEST_CHECK(std::string_view(popped.str, (size_t)popped.len) == result.at(n));
const guf_str_view res = {.str = result.at(n).data(), .len = (ptrdiff_t)result.at(n).size()};
TEST_CHECK(guf_str_view_equal(&popped, &res));
TEST_CHECK(guf_str_view_equal_val_arg(popped, res));
// std::cout << "guf: " << std::string_view{popped.str, (size_t)popped.len} << "\n";
// std::cout << "cpp: " << std::string_view{res.str, (size_t)res.len} << "\n";
++n;
} while (src.len > 0);
TEST_CHECK(n == result.size());
return result;
}
std::vector<std::string_view> StrTest::get_toks(std::string_view sv_in, const std::vector<std::string_view>& delims_in, bool preserve_delims, guf_str_tok_delim_opt opt)
{
const guf_str_view sv = guf_str_view{.str = sv_in.data(), .len = (ptrdiff_t)sv_in.size()};
std::vector<guf_str_view> delims;
for (const auto delim : delims_in) {
delims.push_back(guf_str_view{.str = delim.data(), .len = (ptrdiff_t)delim.size()});
}
guf_str_tok_state tok_state = guf_str_tok_state_new(sv, delims.data(), std::ssize(delims), opt);
std::vector<std::string_view> toks_out;
while (guf_str_tok_next(&tok_state, preserve_delims)) {
if (tok_state.cur_tok.len > 0) {
toks_out.push_back( std::string_view{tok_state.cur_tok.str, (size_t)tok_state.cur_tok.len});
}
if (preserve_delims && tok_state.cur_delim.len > 0) {
toks_out.push_back( std::string_view{tok_state.cur_delim.str, (size_t)tok_state.cur_delim.len});
}
}
TEST_CHECK(tok_state.done);
const ptrdiff_t num_toks = preserve_delims ? tok_state.num_delims_read + tok_state.num_toks_read : tok_state.num_toks_read;
TEST_CHECK(num_toks == std::ssize(toks_out));
return toks_out;
}

View File

@ -1,33 +0,0 @@
#pragma once
#include <vector>
#include <string>
#include "test.hpp"
extern "C"
{
#include "guf_str.h"
#include "guf_alloc_libc.h"
}
struct StrTest : public Test
{
StrTest(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 4, "StrTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
};
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
void test_init_free(std::string str);
void test_init_empty();
void test_append_char(std::string str, bool include_null = false);
void append_str(const std::string& a, const std::string& b);
std::vector<std::string_view> test_popsplit(std::string_view str, std::string_view delim);
std::vector<std::string_view> get_toks(std::string_view sv_in, const std::vector<std::string_view>& delims_in, bool preserve_delims = false, guf_str_tok_delim_opt opt = GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
};

View File

@ -1,387 +0,0 @@
#include "test_utf8.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
#include "guf_str.h"
#include "impls/dict_impl.h"
}
/*
UTF8Test:
*/
void UTF8Test::run()
{
if (done) {
return;
}
push_check_name("read_utf8_chars");
ptrdiff_t valid = 0, invalid = 0;
read_utf8_chars(TEST_DATA_DIR "/" "utf8-test.txt", &valid, &invalid);
TEST_CHECK(valid == 2635 && invalid == 0);
read_utf8_chars(TEST_DATA_DIR "/" "bartleby.txt", &valid, &invalid);
TEST_CHECK(valid > 16000 && invalid == 0);
pop_check_name();
push_check_name("count_words");
dbuf_str_view delims = dbuf_str_view_new(&allocator);
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_WHITESPACE); ++i) {
guf_str_view d = {.str = GUF_UTF8_WHITESPACE[i], .len = (ptrdiff_t)strlen(GUF_UTF8_WHITESPACE[i])};
dbuf_str_view_push_val(&delims, d);
}
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_COMMON_PUNCT); ++i) {
guf_str_view d = {.str = GUF_UTF8_COMMON_PUNCT[i], .len = (ptrdiff_t)strlen(GUF_UTF8_COMMON_PUNCT[i])};
dbuf_str_view_push_val(&delims, d);
}
int words = count_words(TEST_DATA_DIR "/" "utf8-test.txt", &delims);
TEST_CHECK(words == 422);
int words_with_delims = count_words_with_delims(TEST_DATA_DIR "/" "utf8-test.txt", &delims);
TEST_CHECK(words_with_delims == 950);
int words2 = count_words(TEST_DATA_DIR "/" "bartleby.txt", &delims);
TEST_CHECK(words2 > 2048);
dbuf_str_view_free(&delims, NULL);
pop_check_name();
push_check_name("encode_decode");
encode_decode();
encode_decode_file(TEST_DATA_DIR "/" "utf8-test.txt");
encode_decode_file(TEST_DATA_DIR "/" "bartleby.txt");
pop_check_name();
//guf_alloc_tracker_print(&allocator_ctx.tracker, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
}
bool UTF8Test::load_text(const char *fname)
{
FILE *in_file {nullptr};
if (!in_file) {
in_file = fopen(fname, "r");
}
if (!in_file) {
return false;
}
dbuf_char_init(&text_buf, 128, &allocator);
int c = EOF;
while ((c = fgetc(in_file)) != EOF) {
dbuf_char_push_val(&text_buf, (char)c);
text_vec.push_back((char)c);
}
fclose(in_file);
return TEST_CHECK(std::ssize(text_vec) == text_buf.size);
}
void UTF8Test::free_text()
{
dbuf_char_free(&text_buf, NULL);
text_vec.clear();
}
void UTF8Test::read_utf8_chars(const char *fname, ptrdiff_t *n_valid, ptrdiff_t *n_invalid)
{
GUF_ASSERT_RELEASE(load_text(fname));
ptrdiff_t valid_chars = 0, invalid_chars = 0, bytes = 0;
guf_str_view input_str = {.str = text_buf.data, .len = text_buf.size};
guf_utf8_char ch = {};
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
++valid_chars;
// printf("%s", ch.bytes);
} else {
++invalid_chars;
// printf("::INVALID_UTF8_CHAR::");
}
bytes += guf_utf8_char_num_bytes(&ch);
}
TEST_CHECK(input_str.len == 0 && input_str.str == NULL);
TEST_CHECK(bytes == text_buf.size);
// printf("\nread %td bytes\n", bytes);
// printf("read %td valid and %td invalid utf-8 characters\n", valid_chars, invalid_chars);
free_text();
if (n_valid)
*n_valid = valid_chars;
if (n_invalid)
*n_invalid = invalid_chars;
}
int UTF8Test::count_words(const char *fname, const dbuf_str_view *delims)
{
GUF_ASSERT_RELEASE(load_text(fname));
int num_words = 0;
guf_str_tok_state tok_state = guf_str_tok_state_new(guf_str_view{.str = text_buf.data, .len = text_buf.size}, delims->data, delims->size, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
while (guf_str_tok_next(&tok_state, false)) {
TEST_CHECK(tok_state.cur_tok.len > 0);
++num_words;
}
free_text();
return num_words;
}
int UTF8Test::count_words_with_delims(const char *fname, const dbuf_str_view *delims)
{
GUF_ASSERT_RELEASE(load_text(fname));
int num_words = 0, num_delims = 0;
guf_str_tok_state tok_state = guf_str_tok_state_new(guf_str_view{.str = text_buf.data, .len = text_buf.size}, delims->data, delims->size, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
while (guf_str_tok_next(&tok_state, true)) {
if (tok_state.cur_tok.len) {
++num_words;
// printf("'%.*s'\n", (int)tok_state.cur_tok.len, tok_state.cur_tok.str);
}
if (tok_state.cur_delim.len) {
++num_delims;
// if (tok_state.cur_delim.str[0] == '\n')
// printf("'\\n'\n");
// else
// printf("'%.*s'\n", (int)tok_state.cur_delim.len, tok_state.cur_delim.str);
}
}
free_text();
return num_words + num_delims;
}
void UTF8Test::encode_decode_file(const char *fname)
{
GUF_ASSERT_RELEASE(load_text(fname));
dbuf_i32 cp_buf = dbuf_i32_new(&allocator);
ptrdiff_t valid_chars = 0, invalid_chars = 0;
guf_str_view input_str = {.str = text_buf.data, .len = text_buf.size};
guf_utf8_char ch = {};
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
++valid_chars;
const int32_t codepoint = guf_utf8_decode(&ch);
TEST_CHECK(codepoint >= 0);
dbuf_i32_push_val(&cp_buf, codepoint);
} else {
++invalid_chars;
const int32_t codepoint = guf_utf8_decode(&ch);
TEST_CHECK(codepoint < 0);
dbuf_i32_push_val(&cp_buf, -1);
}
}
TEST_CHECK(cp_buf.size == valid_chars + invalid_chars);
guf_str_view in_str = {.str = text_buf.data, .len = text_buf.size};
GUF_CNT_FOREACH(&cp_buf, dbuf_i32, it) {
GUF_ASSERT_RELEASE(it.ptr);
const int32_t codepoint = *it.ptr;
guf_utf8_char utf8_ch = {};
const guf_utf8_stat stat = guf_utf8_char_next(&utf8_ch, &in_str);
if (codepoint >= 0) {
TEST_CHECK(stat == GUF_UTF8_READ_VALID);
guf_utf8_char encoded_ch = {};
TEST_CHECK(guf_utf8_encode(&encoded_ch, codepoint));
TEST_CHECK(guf_utf8_equal(&encoded_ch, &utf8_ch));
}
}
guf_utf8_char utf8_ch = {};
const guf_utf8_stat stat = guf_utf8_char_next(&utf8_ch, &in_str);
TEST_CHECK(stat == GUF_UTF8_READ_DONE);
dbuf_i32_free(&cp_buf, NULL);
free_text();
}
void UTF8Test::encode_decode()
{
guf_utf8_char utf8 = {0};
// 1 byte characters.
for (uint8_t ascii = 0; ascii <= 0x7F; ++ascii) {
TEST_CHECK(guf_utf8_encode(&utf8, ascii));
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 1);
TEST_CHECK(utf8.bytes[0] == ascii);
TEST_CHECK(utf8.bytes[1] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == ascii);
}
// 2 byte characters:
TEST_CHECK(guf_utf8_encode(&utf8, 0x00E6)); // "æ" (Latin Small Letter Ae)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xA6');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00E6);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00E5)); // "å" (Latin Small Letter A with Ring Above)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xA5');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00E5);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00F8)); // "ø" (Latin Small Letter O with Stroke)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xB8');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00F8);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00E4)); // "ä" (Latin Small Letter A with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xA4');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00E4);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00F6)); // "ö" (Latin Small Letter O with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xB6');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00F6);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00D6)); // "Ö" (Latin Capital Letter O with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\x96');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00D6);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00FC)); // "ü" (Latin Small Letter U with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xBC');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00FC);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00B5)); // "µ" (Micro Sign)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC2' && utf8.bytes[1] == '\xB5');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00B5);
TEST_CHECK(guf_utf8_encode(&utf8, 0x030A)); // "◌̊" (Combining Ring Above)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xCC' && utf8.bytes[1] == '\x8A');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x030A);
// 3 byte characters:
TEST_CHECK(guf_utf8_encode(&utf8, 0x7121)); // "無" (Nothingness; CJK Unified Ideograph-7121)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE7' && utf8.bytes[1] == '\x84' && utf8.bytes[2] == '\xA1');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x7121);
TEST_CHECK(guf_utf8_encode(&utf8, 0x201E)); // "„" (Double Low-9 Quotation Mark)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE2' && utf8.bytes[1] == '\x80' && utf8.bytes[2] == '\x9E');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x201E);
TEST_CHECK(guf_utf8_encode(&utf8, 0x20AC)); // "€" (Euro Sign)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE2' && utf8.bytes[1] == '\x82' && utf8.bytes[2] == '\xAC');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x20AC);
TEST_CHECK(guf_utf8_encode(&utf8, 0xFC51)); // "ﱑ" (Arabic Ligature Heh with Jeem Isolated Form)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xEF' && utf8.bytes[1] == '\xB1' && utf8.bytes[2] == '\x91');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0xFC51);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1AA3)); // "᪣" (Tai Tham Sign Keow)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE1' && utf8.bytes[1] == '\xAA' && utf8.bytes[2] == '\xA3');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1AA3);
TEST_CHECK(guf_utf8_encode(&utf8, GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT)); // "<22>" (Replacement Character)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(utf8.bytes[0] == '\xEF' && utf8.bytes[1] == '\xBF' && utf8.bytes[2] == '\xBD');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
// 4 byte characters:
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F308)); // "🌈" (Rainbow)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\x8C' && utf8.bytes[3] == '\x88');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F308);
TEST_CHECK(guf_utf8_encode(&utf8, 0x130B8)); // "𓂸" (Egyptian Hieroglyph D052)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x93' && utf8.bytes[2] == '\x82' && utf8.bytes[3] == '\xB8');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x130B8);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F97A)); // "🥺" (Face with Pleading Eyes)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\xA5' && utf8.bytes[3] == '\xBA');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F97A);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F980)); // "🦀" (Crab)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\xA6' && utf8.bytes[3] == '\x80');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F980);
// Invalid characters:
utf8 = {.bytes = {'\xC0', '\x80', 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0);
utf8 = {.bytes = {'\xC0', 0, 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0);
utf8 = {.bytes = {'\x80', 0, 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0);
// "The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF" (surrogate pairs).
TEST_CHECK(!guf_utf8_encode(&utf8, 0xD800));
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
TEST_CHECK(!guf_utf8_encode(&utf8, 0xDFFF));
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
TEST_CHECK(!guf_utf8_encode(&utf8, 0xDA00));
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
char buf[] = {'\x2F', '\xC0', '\xAE', '\x2E', '\x2F'};
guf_str_view input_str = {.str = buf, .len = GUF_ARR_SIZE(buf)};
guf_utf8_char ch = {};
int valid_chars = 0, invalid_chars = 0;
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
++valid_chars;
} else {
++invalid_chars;
}
}
TEST_CHECK(invalid_chars == 2 && valid_chars == 3);
char buf2[] = {'\xE0', '\x80', 'a', 'b', 'c'}; // 1 invalid 3-byte-character, 2 valid 1-byte-characters
input_str = {.str = buf2, .len = GUF_ARR_SIZE(buf2)};
ch = {};
valid_chars = invalid_chars = 0;
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
// printf("%s", ch.bytes);
++valid_chars;
} else {
// printf("%s", GUF_UTF8_REPLACEMENT_CHAR.bytes);
++invalid_chars;
}
}
TEST_CHECK(invalid_chars == 1 && valid_chars == 2);
}

View File

@ -1,36 +0,0 @@
#pragma once
#include <vector>
#include "test.hpp"
extern "C"
{
#include "impls/dbuf_impl.h"
#include "guf_alloc_libc.h"
}
struct UTF8Test : public Test
{
UTF8Test(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 5, "UTF8Test_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
};
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
dbuf_char text_buf {};
std::vector<char> text_vec;
bool load_text(const char *fname);
void free_text();
void read_utf8_chars(const char *fname, ptrdiff_t *n_valid, ptrdiff_t *n_invalid);
int count_words(const char *fname, const dbuf_str_view *delims);
int count_words_with_delims(const char *fname, const dbuf_str_view *delims);
void encode_decode_file(const char *fname);
void encode_decode();
};