Compare commits

52 Commits

Author SHA1 Message Date
roodletoof 6a6f29f3a6 undef map.c macros at the end of file 2026-05-28 00:42:22 +02:00
roodletoof 459af00c16 some test for hash function 2026-05-27 22:37:51 +02:00
roodletoof ed9e69bc06 make tag recepie that only builds local tags 2026-05-26 00:04:41 +02:00
roodletoof 8be1c2254b some more tests 2026-05-25 21:11:46 +02:00
roodletoof b2ba81c40a remove valgrind for now 2026-05-25 20:43:04 +02:00
roodletoof e1bd55e470 some updates and tests for the equals function 2026-05-25 20:42:53 +02:00
roodletoof 5ea8d2ea32 remove foobar 2026-05-25 19:40:54 +02:00
roodletoof edcd8aaa1d Merge branch 'main' into feature/hashmap 2026-05-20 19:55:12 +02:00
roodletoof 6b36507a00 latest latest non-functioning hashmap 2026-05-10 20:53:50 +02:00
Ivar Fatland 2fd707741b latest non functioning hasmap 2026-05-10 00:16:02 +02:00
roodletoof 1c19b4435c update justfile to not make so much garbage 2026-05-06 20:52:46 +02:00
roodletoof 7bbe0747c5 delete makefile 2026-05-06 19:42:48 +02:00
roodletoof 3b980dd4e4 maybe teeny tiny progress
foudn bug where I was passing around void ** rather than void * into
some places
2026-05-05 23:09:54 +02:00
roodletoof d0d98170ea made a discovery of some kind of fuckup 2026-05-05 23:05:53 +02:00
roodletoof 5dcddd3474 new way of breaking. nice :) 2026-04-25 01:12:13 +02:00
roodletoof 6289039ed4 separate heap locations for mapping arr and items arr 2026-04-25 01:08:03 +02:00
roodletoof a0e3fa8503 rename tags to tag 2026-04-25 01:07:50 +02:00
roodletoof 0a6e089581 some slight progress
map/set still buggy
2026-04-18 16:36:11 +02:00
roodletoof 7ddd7707db still nothing 2026-04-17 15:00:47 +02:00
roodletoof 7d39b21c98 some progress, but broken 2026-04-17 14:54:10 +02:00
roodletoof 9bc0102501 WIP 2026-04-17 14:18:24 +02:00
roodletoof 64829d2ba2 add debugging function
want to visualize the mapping array of maps to see that they work as
intended
2026-04-17 13:17:29 +02:00
roodletoof 81d7f6a067 remove resolved TODOs 2026-04-13 18:34:42 +02:00
roodletoof 1d6355a216 add new todo 2026-04-13 17:42:18 +02:00
roodletoof ff5815887e Revert "set the standard to c99"
This reverts commit 8e6f44323e.
2026-04-13 17:36:36 +02:00
roodletoof 8e6f44323e set the standard to c99 2026-04-12 23:08:01 +02:00
roodletoof 84f982329e more map progress 2026-04-12 22:59:50 +02:00
roodletoof 9a07b00a2c latest 2026-04-12 11:15:24 +02:00
roodletoof 83d66aaf36 good progress on maps 2026-04-12 10:51:01 +02:00
roodletoof 6aa10199a5 some progress on map hash 2026-04-09 22:27:29 +02:00
roodletoof 96c25dc3ad some progress, but mainly convert the TODO's into static_assert(0, ...) 2026-04-07 22:48:51 +02:00
roodletoof da4de1c560 some progress or whatever (still WIP) 2026-03-31 22:00:09 +02:00
roodletoof 253e095e4d some progress on scanner. begin clusterfuck map 2026-03-29 21:50:59 +02:00
roodletoof e41ef17d6b latest 2026-03-29 18:40:26 +02:00
roodletoof e5331e8e81 some broken progress 2026-03-26 19:55:12 +01:00
roodletoof f0d7f0ecb6 working my way through unfucking the fuckery 2026-03-26 19:01:56 +01:00
roodletoof 9e99293c56 foobar 2026-03-26 14:08:35 +01:00
roodletoof 0d5a2b8115 wreck everyting 2026-03-25 22:11:16 +01:00
roodletoof de5a5cd9c2 initial scanner changes draft 2026-03-24 23:52:05 +01:00
roodletoof d34fec63a0 some progress again (maps) 2026-03-04 00:12:32 +01:00
roodletoof 70f9cef280 some progress 2026-02-28 23:54:02 +01:00
roodletoof ac4912abc5 rename hashmap to map 2026-02-28 00:29:58 +01:00
roodletoof 9140019767 hashmap init seems to be done
missing any testing though
2026-02-28 00:05:18 +01:00
roodletoof 24dd5250f8 make it compile on zig compier
cannot access null field members, also cannot access field that is
misaligned. pretend there is something at high multiple of 2 value to
future proof to stupid degree and stop the zig compiler from complaining
about the cool hack
2026-02-26 20:26:05 +01:00
roodletoof 0f7f092ad3 a little bit of this. a little bit of that (hashmap stuff) 2026-02-26 20:14:44 +01:00
roodletoof fbb340d465 WIP hashmap 2026-02-24 22:42:48 +01:00
roodletoof fdb5bb390c added important TODO 2026-02-18 21:15:33 +01:00
roodletoof 8fa5e8c0bf started work on hashmaps/hashsets 2026-02-18 21:09:48 +01:00
roodletoof f726956c78 remove cringe capitalization 2026-02-12 21:38:23 +01:00
roodletoof 0c50c5f0f7 also help on flags 2026-02-12 21:30:26 +01:00
roodletoof 95a1d8eeff lots to do with easing 2026-01-02 01:09:00 +01:00
roodletoof 941baafb01 easing declarations 2026-01-02 00:56:15 +01:00
17 changed files with 878 additions and 352 deletions
+2
View File
@@ -0,0 +1,2 @@
CompileFlags:
Add: [-xc, -std=c99]
+2
View File
@@ -1,2 +1,4 @@
tags
*.un~
/foobar
/foobar-expanded.c
+194 -40
View File
@@ -9,11 +9,12 @@
#include <string.h>
typedef union any_align { char c; int i; long l; long long ll; float f; double d; void *p; long double ld; } any_align_t;
#define MAX_ALIGN ((size_t) sizeof(any_align_t))
#define ALIGN_OF(TYPE) ((size_t)(&((struct{char c; TYPE t;}*) 256)->t) - 256)
#define MAX_ALIGN (ALIGN_OF(any_align_t))
#define KB (1024)
#define MB (KB * KB)
#define GB (KB * KB * KB)
#define PTR_FROM_FIELD_PTR(STRUCT, FIELD, PTR) ((STRUCT *) (((char *) PTR) - offsetof(STRUCT, FIELD)))
#define PTR_FROM_FIELD_PTR(STRUCT, FIELD, PTR) ((STRUCT *) (((char *) (PTR)) - offsetof(STRUCT, FIELD)))
// Contains all operations an allocator can do. Similar interface to sdtlibs
// malloc, realloc and free.
@@ -137,7 +138,7 @@ allocator_t allocator_from_arena(arena_allocator_t *this);
// dynamic arrays //////////////////////////////////////////////////////////////
typedef struct dyn_array_header {
size_t n_items, capacity, itemsize;
size_t n_items, capacity, item_size;
allocator_t allocator;
union {
// allocates a little more memory than needed, but who cares?
@@ -148,20 +149,29 @@ typedef struct dyn_array_header {
typedef struct dyn_array_create_func_args {
allocator_t allocator;
size_t itemsize;
size_t item_size;
size_t initial_capacity;
const char *file;
int line;
} dyn_array_create_func_args_t;
void *dyn_array_create_func(dyn_array_create_func_args_t args);
#define make_arr(TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \
.itemsize = sizeof(TYPE), \
#define arr_make(TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \
.item_size = sizeof(TYPE), \
.file = __FILE__, \
.line = __LINE__, \
.allocator = __VA_ARGS__, \
}))
#define arr_init(PTR, ...) do { \
(*(PTR)) = dyn_array_create_func((dyn_array_create_func_args_t){ \
.item_size = sizeof(*(*(PTR))), \
.file = __FILE__, \
.line = __LINE__, \
.allocator = __VA_ARGS__, \
}); \
} while(0)
// Always reassign the array. if multiple variables reference the same growing
// array, then you should be using pointer pointers.
void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int line);
@@ -223,6 +233,107 @@ bool dyn_array_contains_eq_func(void *this, uint8_t *value, dyn_array_eq_fn eq);
typedef int (*dyn_array_cmp_fn)(const void *a, const void *b);
void arr_qsort(void *this, dyn_array_cmp_fn cmp_fn);
// maps ////////////////////////////////////////////////////////////////////
typedef uint32_t (*key_hash_func_t)(const void *key);
typedef bool (*key_equals_func_t)(const void *key1, const void *key2);
typedef struct map_header {
uint32_t capacity;
uint32_t item_size;
uint32_t key_size;
uint32_t mapping_capacity;
uint32_t n_items;
uint32_t key_offset;
allocator_t allocator;
key_hash_func_t hash;
key_equals_func_t equals;
int32_t *mapping_arr;
union {
uint8_t bytes[1];
any_align_t _[1];
};
} map_header_t;
typedef struct map_create_func_args {
allocator_t allocator;
const char *file;
key_hash_func_t hash; // OPTIONAL
key_equals_func_t equals; // OPTIONAL
unsigned int initial_capacity; // OPTIONAL
unsigned int item_size;
unsigned int key_offset;
unsigned int key_size;
int line;
} map_create_func_args_t;
void *map_create_func(map_create_func_args_t args);
#define map_init(PTR, ...) do { \
(*(PTR)) = map_create_func((map_create_func_args_t) { \
.item_size=sizeof(*(*(PTR))), \
.key_size=sizeof((*(PTR))->key), \
.key_offset=offsetof((*(PTR))->key), \
.file=__FILE__, \
.line=__LINE__, \
.allocator=__VA_ARGS__ \
}); \
} while(0)
#define set_init(PTR, ...) do { \
(*(PTR)) = map_create_func((map_create_func_args_t) { \
.item_size=sizeof(*(*(PTR))), \
.key_size=sizeof(*(*(PTR))), \
.key_offset=0, \
.file=__FILE__, \
.line=__LINE__, \
.allocator=__VA_ARGS__ \
}); \
} while(0)
int map_len_func(void *this);
#define map_len(THIS) map_len_func((void*) THIS)
int map_cap_func(void *this);
#define map_cap(THIS) map_cap_func((void*) THIS)
uint32_t fnv_1a(const uint8_t *bytes, const unsigned int size);
// Used for debugging the mapping array. Prints out nerdfonts icons to indicate
// the state of each mapping slot.
void map_print_mapping_state(void *this, FILE *file);
// Checks that the map can handle one more item. The maps capacity grows if not,
// usually by doubling the capacity.
void map_assure_growable_by_1(void **this, const char *file, int line);
typedef struct index_pair {
unsigned int new_i_into_mapping;
unsigned int old_i_into_mapping;
bool has_old;
} index_pair_t;
/*
* Hashes modulo the header->mapping_capacity. Will linearly probe until a free
* slot is found. If the key already exists in the mapping array, that will be
* indicated in the return value, and the user is expected to overwrite the old
* index with a tombstone if they are going to write a new value for that key.
*/
index_pair_t map_pair_hash(void *this, const uint8_t *pair);
bool map_key_equals(map_header_t *header, const uint8_t *key1_bytes, const uint8_t *key2_bytes);
unsigned int map_place(void *this, index_pair_t idx_pair);
// Exists to take a hash of a key and modulo it so it fits within the maps
// capacity.
uint32_t map_mod_index(const map_header_t *header, uint32_t i);
uint32_t map_hash_key(const map_header_t *header, const uint8_t *key);
// TODO: NOT DONE!!! FIRST try to get the existing in
#define set_add(THIS, VALUE) do { \
map_assure_growable_by_1((void**)THIS, __FILE__, __LINE__); \
unsigned int len = map_len(*(THIS)); \
(*(THIS))[len] = VALUE; \
index_pair_t idx_pair = map_pair_hash((*(THIS)), (const uint8_t*) (&(*(THIS))[len])); \
(*(THIS))[map_place((*(THIS)), idx_pair)] = VALUE; \
} while (0)
// CLI /////////////////////////////////////////////////////////////////////////
#define CLI_UNIQUE1 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__
@@ -268,12 +379,7 @@ int cli_req_int_func(args_t args, const char *flag_name, const char *file, int l
) \
if (CLI_UNIQUE1)
// scanner /////////////////////////////////////////////////////////////////////
typedef struct error_node {
char *msg;
struct error_node *next;
} error_node_t;
// scan /////////////////////////////////////////////////////////////////////
typedef union scan_value {
int digit;
@@ -291,41 +397,50 @@ typedef union scan_value {
char *string_literal;
} scan_value_t;
typedef struct scanner {
typedef struct scan {
const char *name; // name of the buffer
const char *start; // pointer to the full buffer
const char *cur; // current pointer
scan_value_t value;
error_node_t *errors; // singly linked list of error strings
allocator_t allocator;
} scanner_t;
} scan_t;
scanner_t make_scanner(const char *name, const char *buffer, allocator_t allocator);
void scanner_recover(scanner_t *s);
void scanner_error(scanner_t *s, const char *message);
void scanner_error_and_recover(scanner_t *s, const char *message);
bool scan_eof(scanner_t *s);
bool scan_literal(scanner_t *s, const char *lit);
int scan_repeat_literal(scanner_t *s, const char *lit);
bool scan_whitespace(scanner_t *s);
bool scan_digit(scanner_t *s);
bool scan_i64(scanner_t *s);
bool scan_i32(scanner_t *s);
bool scan_i16(scanner_t *s);
bool scan_i8(scanner_t *s);
bool scan_u64(scanner_t *s);
bool scan_u32(scanner_t *s);
bool scan_u16(scanner_t *s);
bool scan_u8(scanner_t *s);
bool scan_f64(scanner_t *s);
bool scan_f32(scanner_t *s);
typedef struct scan_error {
const char *filename;
int line;
int column;
const char *message;
} scan_error_t;
typedef struct scan_result {
bool ok;
union {
scan_error_t error;
scan_value_t value;
};
} scan_result_t;
scan_t make_scan(const char *name, const char *buffer, allocator_t allocator);
void scan_next_line(scan_t *s);
scan_result_t scan_eof(scan_t *s);
scan_result_t scan_literal(scan_t *s, const char *lit);
int scan_repeat_literal(scan_t *s, const char *lit);
scan_result_t scan_whitespace(scan_t *s);
scan_result_t scan_digit(scan_t *s);
scan_result_t scan_i64(scan_t *s);
scan_result_t scan_i32(scan_t *s);
scan_result_t scan_i16(scan_t *s);
scan_result_t scan_i8(scan_t *s);
scan_result_t scan_u64(scan_t *s);
scan_result_t scan_u32(scan_t *s);
scan_result_t scan_u16(scan_t *s);
scan_result_t scan_u8(scan_t *s);
scan_result_t scan_f64(scan_t *s);
scan_result_t scan_f32(scan_t *s);
// Scan as much of an identifier as possible, you are responsible to scan for
// valid characters after the identifier is scanned.
bool scan_identifier(scanner_t *s);
bool scan_string_literal(scanner_t *s);
bool scanner_print_errors(scanner_t *s, FILE *fp);
bool looks_like_float(scanner_t *s);
bool looks_like_int(scanner_t *s);
scan_result_t scan_identifier(scan_t *s);
scan_result_t scan_string_literal(scan_t *s);
scan_value_t required(scan_result_t res);
// string builder //////////////////////////////////////////////////////////////
typedef struct string_builder_node {
@@ -359,6 +474,44 @@ void sb_fprint_and_clear(string_builder_t *this, FILE *dest);
char *read_entire_file(const char *path, allocator_t allocator);
char **read_all_file_lines(const char *path, allocator_t allocator);
// easing //////////////////////////////////////////////////////////////////////
float ease_in_quad(float t);
float ease_out_quad(float t);
float ease_inout_quad(float t);
float ease_in_cubic(float t);
float ease_out_cubic(float t);
float ease_inout_cubic(float t);
float ease_in_quart(float t);
float ease_out_quart(float t);
float ease_inout_quart(float t);
float ease_in_quint(float t);
float ease_out_quint(float t);
float ease_inout_quint(float t);
float ease_in_expo(float t);
float ease_out_expo(float t);
float ease_inout_expo(float t);
float ease_in_circ(float t);
float ease_out_circ(float t);
float ease_inout_circ(float t);
float ease_in_back(float t);
float ease_out_back(float t);
float ease_inout_back(float t);
float ease_in_elastic(float t);
float ease_out_elastic(float t);
float ease_inout_elastic(float t);
float ease_in_bounce(float t);
float ease_out_bounce(float t);
float ease_inout_bounce(float t);
#ifdef CIG_IMPL
void *allocator_alloc_func(allocator_t this, size_t bytes, const char *file, int line) {
@@ -390,6 +543,7 @@ void allocator_reset(allocator_t this) {
#include "scanner.c"
#include "string_builder.c"
#include "file_io.c"
#include "map.c"
#endif // CIG_IMPL
+8 -8
View File
@@ -5,13 +5,13 @@
void *dyn_array_create_func(dyn_array_create_func_args_t args) {
dyn_array_header_t *header = allocator_alloc_func(
args.allocator,
sizeof(dyn_array_header_t) + args.itemsize * args.initial_capacity,
sizeof(dyn_array_header_t) + args.item_size * args.initial_capacity,
args.file,
args.line
);
header->n_items = 0;
header->capacity = args.initial_capacity;
header->itemsize = args.itemsize;
header->item_size = args.item_size;
header->allocator = args.allocator;
return &header->bytes;
}
@@ -29,7 +29,7 @@ void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int
header = allocator_resize_func(
header->allocator,
header,
sizeof(dyn_array_header_t) + header->itemsize * new_capacity,
sizeof(dyn_array_header_t) + header->item_size * new_capacity,
file,
line
);
@@ -71,8 +71,8 @@ bool dyn_array_contains_func(void *this, uint8_t *value) {
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
for (size_t i = 0; i < header->n_items; i++) {
bool is_equal = true;
for (size_t off = 0; off < header->itemsize; off++) {
if (header->bytes[i*header->itemsize+off] != value[off]) {
for (size_t off = 0; off < header->item_size; off++) {
if (header->bytes[i*header->item_size+off] != value[off]) {
is_equal = false;
break;
}
@@ -86,10 +86,10 @@ bool dyn_array_contains_func(void *this, uint8_t *value) {
bool dyn_array_contains_eq_func(void *this, uint8_t *value, dyn_array_eq_fn eq) {
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
size_t itemsize = header->itemsize;
size_t item_size = header->item_size;
for (size_t i = 0; i < header->n_items; i++) {
void *element = &header->bytes[i * itemsize];
void *element = &header->bytes[i * item_size];
if (eq(element, value)) {
return true;
}
@@ -116,5 +116,5 @@ void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int
void arr_qsort(void *this, dyn_array_cmp_fn cmp_fn) {
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
qsort(this, header->n_items, header->itemsize, cmp_fn);
qsort(this, header->n_items, header->item_size, cmp_fn);
}
+98
View File
@@ -0,0 +1,98 @@
#include "cig.h"
static float clamp(float t, float min, float max) {
if (t < min) {
return min;
}
if (max < t) {
return max;
}
return t;
}
static float norm(float t) {
return clamp(t, 0.0f, 1.0f);
}
float ease_in_quad(float t) {
t = norm(t);
return t * t;
}
float ease_out_quad(float t) {
}
float ease_inout_quad(float t) {
}
float ease_in_cubic(float t) {
}
float ease_out_cubic(float t) {
}
float ease_inout_cubic(float t) {
}
float ease_in_quart(float t) {
}
float ease_out_quart(float t) {
}
float ease_inout_quart(float t) {
}
float ease_in_quint(float t) {
}
float ease_out_quint(float t) {
}
float ease_inout_quint(float t) {
}
float ease_in_expo(float t) {
}
float ease_out_expo(float t) {
}
float ease_inout_expo(float t) {
}
float ease_in_circ(float t) {
}
float ease_out_circ(float t) {
}
float ease_inout_circ(float t) {
}
float ease_in_back(float t) {
}
float ease_out_back(float t) {
}
float ease_inout_back(float t) {
}
float ease_in_elastic(float t) {
}
float ease_out_elastic(float t) {
}
float ease_inout_elastic(float t) {
}
float ease_in_bounce(float t) {
}
float ease_out_bounce(float t) {
}
float ease_inout_bounce(float t) {
}
+1 -1
View File
@@ -19,7 +19,7 @@ char *read_entire_file(const char *path, allocator_t allocator) {
// Returns dynamic array, of fixed char strings.
char **read_all_file_lines(const char *path, allocator_t allocator) {
char *contents = read_entire_file(path, allocator);
char **lines = make_arr(char*, allocator);
char **lines = arr_make(char*, allocator);
arr_append(lines, contents);
bool just_split = false;
for (char *c = contents; (*c)!='\0'; c++) {
+6 -24
View File
@@ -1,5 +1,5 @@
CC := "zig cc"
CFLAGS := "-pedantic -Wall -Wextra -Wno-override-init -O0 -g -fno-omit-frame-pointer -fno-inline"
CFLAGS := "-pedantic -Wall -Wextra -Wno-override-init -O0 -g3 -fno-omit-frame-pointer -fno-inline -fsanitize=address,undefined -fno-sanitize-recover=all"
LDFLAGS := if os() == "macos" {
"$(pkg-config --libs --cflags criterion)"
} else {
@@ -12,29 +12,6 @@ TESTBIN := TMP/"all_tests"
set shell := ["bash", "-cu"]
FOOBAR := "foobar"
FOOBAR_SOURCE := FOOBAR+".c"
FOOBAR_BIN := TMP/FOOBAR
FOOBAR_EXPANDED := TMP/FOOBAR+"-expanded.c"
build_foobar2:
{{CC}} {{FOOBAR_SOURCE}} {{CFLAGS}} -o foobar2
run_foobar: build_foobar
{{FOOBAR_BIN}}
build_foobar: foobar_expanded
{{CC}} {{FOOBAR_EXPANDED}} {{CFLAGS}} -o {{FOOBAR_BIN}}
echo "{{FOOBAR_BIN}}"
foobar_expanded:
{{CC}} -P -E {{FOOBAR_SOURCE}} -o {{FOOBAR_EXPANDED}}
clang-format -i {{FOOBAR_EXPANDED}}
clean_foobar:
rm {{FOOBAR_BIN}}
[default]
test: build
{{TESTBIN}}
@@ -51,10 +28,15 @@ build:
DEPENDENCY_HEADERS := if os() == "macos" {
"$(pkg-config --cflags-only-I criterion | sed 's/-I//g')"
} else if os() == "linux" {
"/usr/include/"
} else {
"TODO"
}
tag:
ctags --languages=c --langmap=c:.c.h -R .
tag-all:
echo "Making tags from project and headers in {{DEPENDENCY_HEADERS}}"
ctags --languages=c --langmap=c:.c.h -R {{DEPENDENCY_HEADERS}} .
-38
View File
@@ -1,38 +0,0 @@
CC := zig cc
CFLAGS := -pedantic -Wall -Wextra -Wno-override-init -O0 -g -fno-omit-frame-pointer -fno-inline
LDFLAGS := -lcriterion
TESTBIN := /tmp/all_tests
.PHONY: test scratch
run_test: test
$(TESTBIN)
run_test_memcheck: test
valgrind --leak-check=full --show-leak-kinds=all --trace-children=yes --error-exitcode=1 $(TESTBIN); \
test:
@echo "Discovering test files..."
@files=$$(find . -type f -name 'test*.c'); \
if [ -z "$$files" ]; then \
echo "No test files found!"; \
else \
echo "Compiling all test files into $(TESTBIN)..."; \
$(CC) $(CFLAGS) _allocator_impl.c $$files -o $(TESTBIN) $(LDFLAGS) || exit 1; \
fi
PROJECT_ROOT := $(shell pwd)
PROJECT_C_FILES := $(shell find $(PROJECT_ROOT) \( -name '*.c' -o -name '*.h' \))
CRITERION_HEADERS := $(shell find /usr/include/criterion)
ALL_FILES := $(PROJECT_C_FILES) $(CRITERION_HEADERS)
tags: $(ALL_FILES)
ctags -R $(ALL_FILES)
scratch: /tmp/scratch
valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 /tmp/scratch
/tmp/scratch: scratch.c
gcc scratch.c -o /tmp/scratch
+266
View File
@@ -0,0 +1,266 @@
#include "cig.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#define FLAG_FREE (-1)
#define FLAG_TOMBSTONE (-2)
// TODO: when setting a key to a gravestone, then while the mapping
// item to the right is FLAG_FREE, you can set this one to FLAG_FREE,
// go one index less, and if that is also a FLAG_GRAVESTONE, you can
// continue the process.
#define ESC "\033["
#define RED ESC "31m"
#define DEFAULT ESC "0m"
#define COLORED(COLOR, TEXT) COLOR TEXT DEFAULT
uint32_t map_mod_index(const map_header_t *header, uint32_t i) {
return i % header->mapping_capacity;
}
void map_print_mapping_state(void *this, FILE *file) {
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
for ( unsigned int i = 0; i < header->mapping_capacity; i++ ) {
int mapping = header->mapping_arr[i];
if (mapping == FLAG_TOMBSTONE) {
fprintf(file, "󰮢");
} else if (mapping == FLAG_FREE) {
fprintf(file, "");
} else if (mapping < 0) {
fprintf(file, COLORED(RED, ""));
} else {
fprintf(file, "");
}
}
fprintf(file, "\n");
}
static inline unsigned int mapping_cap(unsigned int capacity) {
return capacity * 2;
}
typedef struct __map_sizes {
size_t header_bytes;
size_t mapping_arr_bytes;
} __map_sizes_t;
__map_sizes_t __map_sizes(size_t capacity, size_t item_size) {
const size_t header_bytes = sizeof(map_header_t) + item_size * capacity;
map_header_t foo;
// TODO: this is shit. I am defining that the mapping arr is 2x the size of
// the items arr in multiple places. Figure this out at some point.
const size_t mapping_arr_bytes = sizeof(*foo.mapping_arr) * capacity * 2;
return (__map_sizes_t){
.header_bytes=header_bytes,
.mapping_arr_bytes=mapping_arr_bytes,
};
}
void *map_create_func(
map_create_func_args_t args
) {
__map_sizes_t sizes = __map_sizes(
args.initial_capacity,
args.item_size
);
map_header_t *header = allocator_alloc_func(
args.allocator,
sizes.header_bytes,
args.file,
args.line
);
header->mapping_arr = allocator_alloc_func(
args.allocator,
sizes.mapping_arr_bytes,
args.file,
args.line
);
header->mapping_capacity = args.initial_capacity * 2;
header->n_items = 0;
header->capacity = args.initial_capacity;
header->item_size = args.item_size;
header->key_size = args.key_size;
header->allocator = args.allocator;
header->hash = args.hash;
header->equals = args.equals;
for ( unsigned int i = 0; i < header->mapping_capacity; i++ ) {
header->mapping_arr[i] = FLAG_FREE;
}
return header->bytes;
}
static const uint8_t *__cig_key_ptr_from_pair_ptr(const map_header_t *this, const uint8_t *pair) {
return (const uint8_t *) (&pair[this->key_offset]);
}
bool map_key_equals(map_header_t *header, const uint8_t *key1_bytes, const uint8_t *key2_bytes) {
if (header->equals != NULL) {
return header->equals(key1_bytes, key2_bytes);
}
for ( unsigned int i = 0; i < header->key_size; i++ ) {
if (key1_bytes[i] != key2_bytes[i]) {
return false;
}
}
return true;
}
unsigned int map_place(void *this, index_pair_t idx) {
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
if (idx.has_old) {
header->mapping_arr[idx.new_i_into_mapping] = header->mapping_arr[idx.old_i_into_mapping];
header->mapping_arr[idx.old_i_into_mapping] = FLAG_TOMBSTONE;
for ( long i = idx.old_i_into_mapping; i != idx.new_i_into_mapping; map_mod_index(header, i-1) ) {
if (header->mapping_arr[i] == FLAG_TOMBSTONE) {
header->mapping_arr[i] = FLAG_FREE;
} else {
break;
}
}
} else {
header->mapping_arr[idx.new_i_into_mapping] = header->n_items;
header->n_items++;
}
return header->mapping_arr[idx.new_i_into_mapping];
}
uint32_t map_hash_key(const map_header_t *header, const uint8_t *key) {
if (header->hash != NULL) {
return map_mod_index(header, header->hash(key));
} else {
return map_mod_index(header, fnv_1a(key, header->key_size));
}
}
typedef struct pair {
const uint8_t *pair;
const size_t pair_len;
const uint8_t *key;
const size_t key_len;
} pair_t;
static pair_t __map_get_pair_at(const map_header_t *this, size_t index) {
const uint8_t *pair = (uint8_t*)(&this->bytes[index * this->item_size]);
const size_t pair_len = this->item_size;
const uint8_t *key = __cig_key_ptr_from_pair_ptr(this, pair);
const size_t key_len = this->key_size;
return (pair_t) {
.pair=pair,
.pair_len=pair_len,
.key=key,
.key_len=key_len,
};
}
index_pair_t map_pair_hash(void *this, const uint8_t *pair) {
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
const uint8_t *new_key = __cig_key_ptr_from_pair_ptr(header, pair);
// find the initial index based on hash
uint32_t i_into_mapping = map_hash_key(header, new_key);
int existing_i_into_bytes = header->mapping_arr[i_into_mapping];
if (existing_i_into_bytes == FLAG_FREE) {
return (index_pair_t) {
.has_old=false,
.new_i_into_mapping=i_into_mapping,
};
}
// TODO THIS!!
}
// TODO: search all occurances of **this. The whole point is to reassign to the
// variable!
void map_assure_growable_by_1(void **this, const char *file, int line) {
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, *this);
unsigned int new_n_items = header->n_items + 1;
if (new_n_items <= header->capacity) {
return;
}
allocator_t allocator = header->allocator;
unsigned int new_capacity = 1;
if (header->capacity > 0) {
new_capacity = header->capacity * 2;
}
__map_sizes_t new_size = __map_sizes(
new_capacity,
header->item_size
);
header = allocator_resize_func(allocator, header, new_size.header_bytes, file, line);
header->mapping_arr = (int*) allocator_resize_func(allocator, header->mapping_arr, new_size.mapping_arr_bytes, file, line);
header->mapping_capacity = new_capacity * 2;
header->capacity=new_capacity;
// first we have to clear the entire mapping array
for ( unsigned int i = 0; i < header->mapping_capacity; i++ ) {
header->mapping_arr[i] = FLAG_FREE;
}
// Then we have to re-hash and map all the items in the existing map.
// The reason for re-hashing is that all hashes are moduloed by the
// size of the mapping array, so the resulting hash will likely change.
for ( unsigned int i = 0; i < header->n_items; i++ ) {
void *pair = &(((uint8_t *)(*this))[i*header->item_size]);
// This is okay, because we absolutely know that the keys don't already
// exist. (assuming all the other code for map is doing it's job)
// TODO: when the header is retrieved within the map_pair_hash function the address is completely wrong. Scrambles the parameters and fucks everyghing!!!
// TODO!!!
// TODO!!!
// TODO!!!
// TODO!!!
unsigned int mapping_index = map_pair_hash(*this, pair).new_i_into_mapping;
header->mapping_arr[mapping_index] = i;
}
// TODO!!!! FUCKING reasign to *this IDIOT!
}
uint32_t fnv_1a(const uint8_t *bytes, const unsigned int size) {
// Yanked from: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
const uint32_t PRIME = 0x01000193;
const uint32_t OFFSET = 0x811c9dc5;
uint32_t hash = OFFSET;
for (unsigned int i = 0; i < size; i++) {
uint32_t byte = (uint32_t)bytes[i];
hash = hash ^ byte;
hash = hash * PRIME;
}
return hash;
}
int map_len_func(void *this) {
if (this == NULL) {
return 0;
}
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
return header->n_items;
}
int map_cap_func(void *this) {
if (this == NULL) {
return 0;
}
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
return header->capacity;
}
#undef FLAG_FREE
#undef FLAG_TOMBSTONE
#undef ESC
#undef RED
#undef DEFAULT
#undef COLORED
View File
+129 -176
View File
@@ -4,19 +4,17 @@
#include <string.h>
#include <errno.h>
scanner_t make_scanner(const char *name, const char *buffer, allocator_t allocator) {
scanner_t s = {
scan_t make_scan(const char *name, const char *buffer, allocator_t allocator) {
scan_t s = {
.name = name,
.start = buffer,
.cur = buffer,
.value = {0},
.errors = NULL,
.allocator = allocator
};
return s;
}
void scanner_recover(scanner_t *s) {
void scan_next_line(scan_t *s) {
while (*s->cur && *s->cur != '\n') {
s->cur++;
}
@@ -24,7 +22,9 @@ void scanner_recover(scanner_t *s) {
s->cur++; // move *past* the newline
}
void scanner_error(scanner_t *s, const char *message) {
// TODO: probably do not need an allocator for a scanner?
scan_error_t scan_error(const scan_t *s, const char *message) {
const char *error_pos = s->cur; // use current scanner position
// Compute line + column (1-based)
int line = 1, col = 1;
@@ -32,79 +32,78 @@ void scanner_error(scanner_t *s, const char *message) {
if (*p == '\n') { line++; col = 1; }
else col++;
}
// need 40 bytes for pair of largest possible 64 bit values, then some for
// ':' and ' ' characters. Just rounded up to 64 because reasons.
size_t length = strlen(s->name) + 1 + strlen(message) + 1 + 64;
char *buf = allocator_alloc(s->allocator, length);
snprintf(
buf,
length,
"%s:%d:%d: %s",
s->name, line, col, message
);
error_node_t *node = allocator_alloc(s->allocator, sizeof(error_node_t));
node->msg = buf;
node->next = NULL;
if (!s->errors) {
s->errors = node;
} else {
error_node_t *p = s->errors;
while (p->next) p = p->next;
p->next = node;
}
return (scan_error_t) {
.filename = s->name,
.line = line,
.column = col,
.message = message
};
}
void scanner_error_and_recover(scanner_t *s, const char *message) {
scanner_error(s, message);
scanner_recover(s);
#define scan_value_result(VAL) ((scan_result_t){.ok=true, .value=(scan_value_t){VAL}})
scan_result_t scan_error_result(const scan_t *s, const char *message) {
return (scan_result_t) {
.ok=false,
.error=scan_error(s, message),
};
}
bool scan_eof(scanner_t *s) {
return *s->cur == '\0';
scan_result_t scan_eof(scan_t *s) {
return (scan_result_t) {
.ok=*s->cur == '\0',
.error=scan_error(s, "null character not found"),
};
}
bool scan_literal(scanner_t *s, const char *lit) {
scan_result_t scan_literal(scan_t *s, const char *lit) {
const char *save = s->cur;
while (*lit && *lit == *s->cur) {
lit++;
s->cur++;
}
if (*lit == '\0') {
return true;
return scan_value_result(0);
}
s->cur = save;
return false;
return scan_error_result(s, "invalid literal");
// TODO: add another optional string field to specify what literal is
// missing
}
int scan_repeat_literal(scanner_t *s, const char *lit) {
int scan_repeat_literal(scan_t *s, const char *lit) {
int n_repeats = 0;
while (scan_literal(s, lit)) {
while (scan_literal(s, lit).ok) {
n_repeats++;
}
return n_repeats;
}
bool scan_whitespace(scanner_t *s) {
scan_result_t scan_whitespace(scan_t *s) {
const char *save = s->cur;
while (isspace((unsigned char)*s->cur)) s->cur++;
return save != s->cur;
return (scan_result_t) {
.ok=save != s->cur,
.error=scan_error(s, "expected whitespace"),
};
}
bool scan_digit(scanner_t *s) {
scan_result_t scan_digit(scan_t *s) {
if (!isdigit((unsigned char)*s->cur)) {
return false;
return scan_error_result(s, "expected digit");
}
s->value.digit = (*s->cur) - '0';
s->cur++;
return true;
return scan_value_result(.digit=(*s->cur) - '0');
}
bool scan_i64(scanner_t *s) {
scan_result_t scan_i64(scan_t *s) {
const char *save = s->cur;
if (*s->cur == '-' || *s->cur == '+') s->cur++;
if (!isdigit((unsigned char)*s->cur)) {
s->cur = save;
return false;
return scan_error_result(s, "is not an integer");
}
s->cur = save;
char *end;
@@ -112,69 +111,63 @@ bool scan_i64(scanner_t *s) {
int64_t val = strtoll(s->cur, &end, 10);
if (end == s->cur) {
s->cur = save;
return false;
return scan_error_result(s, "is not an integer");
}
if (errno == ERANGE) {
scanner_error(s, "integer does not fit in i64 value");
return false;
return scan_error_result(s, "integer does not fit in i64 value");
}
s->cur = end;
s->value.i64 = val;
return true;
return scan_value_result(.i64=val);
}
bool scan_i32(scanner_t *s) {
if (!scan_i64(s)) {
return false;
scan_result_t scan_i32(scan_t *s) {
scan_result_t result_i64 = scan_i64(s);
if (!result_i64.ok) {
return result_i64;
}
int32_t val = (int32_t)s->value.i64;
int32_t val = (int32_t)result_i64.value.i64;
int64_t back = (int64_t)val;
if (back != s->value.i64) {
scanner_error(s, "int does not fit in i32 value");
return false;
if (back != result_i64.value.i64) {
return scan_error_result(s, "integer does not fit in i32 value");
}
s->value.i32 = val;
return true;
return scan_value_result(.i32=val);
}
bool scan_i16(scanner_t *s) {
if (!scan_i64(s)) {
return false;
scan_result_t scan_i16(scan_t *s) {
scan_result_t result_i64 = scan_i64(s);
if (!result_i64.ok) {
return result_i64;
}
int16_t val = (int16_t)s->value.i64;
int16_t val = (int16_t)result_i64.value.i64;
int64_t back = (int64_t)val;
if (back != s->value.i64) {
scanner_error(s, "int does not fit in i16 value");
return false;
if (back != result_i64.value.i64) {
return scan_error_result(s, "integer does not fit in i16 value");
}
s->value.i16 = val;
return true;
return scan_value_result(.i16=val);
}
bool scan_i8(scanner_t *s) {
if (!scan_i64(s)) {
return false;
scan_result_t scan_i8(scan_t *s) {
scan_result_t result_i64 = scan_i64(s);
if (!result_i64.ok) {
return result_i64;
}
int8_t val = (int8_t)s->value.i64;
int8_t val = (int8_t)result_i64.value.i64;
int64_t back = (int64_t)val;
if (back != s->value.i64) {
scanner_error(s, "int does not fit in i8 value");
return false;
if (back != result_i64.value.i64) {
return scan_error_result(s, "integer does not fit in i8 value");
}
s->value.i8 = val;
return true;
return scan_value_result(.i8=val);
}
bool scan_u64(scanner_t *s) {
scan_result_t scan_u64(scan_t *s) {
const char *save = s->cur;
if (*s->cur == '-') {
scanner_error(s, "- is not allowed for unsigned integers");
return false;
return scan_error_result(s, "- is not allowed for unsigned integers");
}
if (*s->cur == '+') s->cur++;
if (!isdigit((unsigned char)*s->cur)) {
s->cur = save;
return false;
return scan_error_result(s, "not an integer");
}
s->cur = save;
char *end;
@@ -182,112 +175,106 @@ bool scan_u64(scanner_t *s) {
uint64_t val = strtoull(s->cur, &end, 10);
if (end == s->cur) {
s->cur = save;
return false;
return scan_error_result(s, "not an integer");
}
if (errno == ERANGE) {
scanner_error(s, "integer does not fit in u64 value");
return false;
return scan_error_result(s, "integer does not fit in i64 value");
}
s->cur = end;
s->value.u64 = val;
return true;
return scan_value_result(.u64=val);
}
bool scan_u32(scanner_t *s) {
if (!scan_u64(s)) {
return false;
scan_result_t scan_u32(scan_t *s) {
scan_result_t result_u64 = scan_u64(s);
if (!result_u64.ok) {
return result_u64;
}
uint32_t val = (uint32_t)s->value.u64;
uint32_t val = (uint32_t)result_u64.value.u64;
uint64_t back = (uint64_t)val;
if (back != s->value.u64) {
scanner_error(s, "int does not fit in u32 value");
return false;
if (back != result_u64.value.u64) {
return scan_error_result(s, "integer does not fit in u32 values");
}
s->value.u32 = val;
return true;
return scan_value_result(.u32=val);
}
bool scan_u16(scanner_t *s) {
if (!scan_u64(s)) {
return false;
scan_result_t scan_u16(scan_t *s) {
scan_result_t result_u64 = scan_u64(s);
if (!result_u64.ok) {
return result_u64;
}
uint16_t val = (uint16_t)s->value.u64;
uint16_t val = (uint16_t)result_u64.value.u64;
uint64_t back = (uint64_t)val;
if (back != s->value.u64) {
scanner_error(s, "int does not fit in u16 value");
return false;
if (back != result_u64.value.u64) {
return scan_error_result(s, "integer does not fit in u16 value");
}
s->value.u16 = val;
return true;
return scan_value_result(.u16=val);
}
bool scan_u8(scanner_t *s) {
if (!scan_u64(s)) {
return false;
scan_result_t scan_u8(scan_t *s) {
scan_result_t result_u64 = scan_u64(s);
if (!result_u64.ok) {
return result_u64;
}
uint8_t val = (uint8_t)s->value.u64;
uint8_t val = (uint8_t)result_u64.value.u64;
uint64_t back = (uint64_t)val;
if (back != s->value.u64) {
scanner_error(s, "int does not fit in u8 value");
return false;
if (back != result_u64.value.u64) {
return scan_error_result(s, "integer does not fit in u8 value");
}
s->value.u8 = val;
return true;
return scan_value_result(.u8=val);
}
bool scan_f64(scanner_t *s) {
scan_result_t scan_f64(scan_t *s) {
const char *save = s->cur;
char *end;
errno = 0;
double val = strtod(s->cur, &end);
if (end == s->cur) {
s->cur = save;
return false;
return scan_error_result(s, "not a float");
}
if (errno == ERANGE) {
scanner_error(s, "float does not fit in f64 value");
return false;
return scan_error_result(s, "float does not fit in f64 value");
}
s->cur = end;
s->value.f64 = val;
return true;
return scan_value_result(.f64=val);
}
bool scan_f32(scanner_t *s) {
if (!scan_f64(s)) {
return false;
scan_result_t scan_f32(scan_t *s) {
scan_result_t result_f64 = scan_f64(s);
if (!result_f64.ok) {
return result_f64;
}
float val = (float)s->value.f64;
float val = (float)result_f64.value.f64;
double back = (double)val;
if (back != s->value.f64) {
scanner_error(s, "float does not fit in f32 value");
return false;
if (back != result_f64.value.f64) {
return scan_error_result(s, "float does not fit in f32 value");
}
s->value.f32 = val;
return true;
return scan_value_result(.f32=val);
}
bool scan_identifier(scanner_t *s) {
scan_result_t scan_identifier(scan_t *s) {
if (!isalpha((unsigned char)*s->cur) && *s->cur != '_') {
return false;
return scan_error_result(s, "not an identifier");
}
const char *start = s->cur++;
while (isalnum((unsigned char)*s->cur) || *s->cur == '_') {
s->cur++;
}
// TODO: use arraylists just to standardize?
size_t len = s->cur - start;
char *buf = allocator_alloc(s->allocator, len + 1);
memcpy(buf, start, len);
buf[len] = '\0';
s->value.identifier = buf;
return true;
return scan_value_result(.identifier=buf);
}
bool scan_string_literal(scanner_t *s) {
scan_result_t scan_string_literal(scan_t *s) {
// TODO: use arraylists just to standardize?
const char *save = s->cur;
if (*s->cur != '"')
return false;
if (*s->cur != '"') {
return scan_error_result(s, "missing opening quote");
}
s->cur++; // skip opening quote
char *buf = allocator_alloc(s->allocator, 256);
size_t cap = 256;
@@ -304,9 +291,8 @@ bool scan_string_literal(scanner_t *s) {
case '"': c = '"'; break;
case '0': c = '\0'; break;
default:
scanner_error(s, "invalid escape sequence");
s->cur = save;
return false;
return scan_error_result(s, "invalid excape sequence");
}
}
if (len + 2 >= cap) {
@@ -316,51 +302,18 @@ bool scan_string_literal(scanner_t *s) {
buf[len++] = c;
}
if (*s->cur != '"') {
scanner_error(s, "unterminated string literal");
s->cur = save;
return false;
return scan_error_result(s, "unterminated string literal");
}
s->cur++; // skip closing quote
buf[len] = '\0';
s->value.string_literal = buf;
return true;
return scan_value_result(.string_literal=buf);
}
bool scanner_print_errors(scanner_t *s, FILE *fp) {
for (error_node_t *node = s->errors; node != NULL; node = node->next) {
fprintf(fp, "%s\n", node->msg);
scan_value_t required(scan_result_t res) {
if (!res.ok) {
fprintf(stderr, "%s:%d:%d: %s", res.error.filename, res.error.line, res.error.column, res.error.message);
exit(1);
}
return s->errors != NULL;
}
bool looks_like_float(scanner_t *s) {
const char *cur = s->cur;
if (
((*cur) == '+') ||
((*cur) == '-')
) {
cur++;
}
if (!isdigit(((unsigned char)*cur))) {
return false;
} else {
cur++;
}
while (isdigit(((unsigned char)*cur))) {
cur++;
}
if ((*cur) != '.') {
return false;
} else {
cur++;
}
return isdigit(((unsigned char)*cur));
}
bool looks_like_int(scanner_t *s) {
const char *cur = s->cur;
if (*cur == '+' || *cur == '-') {
cur++;
}
return isdigit(*cur);
return res.value;
}
+7 -7
View File
@@ -139,7 +139,7 @@ Test(arena_allocator, resize_with_zero_capacity) {
typedef struct {
size_t size;
size_t capacity;
size_t itemsize;
size_t item_size;
allocator_t allocator;
uint8_t bytes[];
} test_header_t;
@@ -148,7 +148,7 @@ Test(arena_allocator, resize_with_zero_capacity) {
test_header_t *header = allocator_alloc(arena, sizeof(test_header_t));
header->size = 0;
header->capacity = 0;
header->itemsize = sizeof(int);
header->item_size = sizeof(int);
// Resize to add space for 1 element
header = allocator_resize(arena, header, sizeof(test_header_t) + sizeof(int) * 1);
@@ -185,7 +185,7 @@ Test(arena_allocator, dyn_array_resize_pattern) {
allocator_t arena = arena_allocator_create(backing, 10 * KB);
allocator_reset(arena);
int *arr = make_arr(int, arena);
int *arr = arr_make(int, arena);
// Add 100 elements, forcing multiple resizes
for (int i = 0; i < 100; i++) {
@@ -206,8 +206,8 @@ Test(arena_allocator, multiple_arrays_resize) {
allocator_t arena = arena_allocator_create(backing, 20 * KB);
allocator_reset(arena);
int *arr1 = make_arr(int, arena);
int *arr2 = make_arr(int, arena);
int *arr1 = arr_make(int, arena);
int *arr2 = arr_make(int, arena);
// Add to arr1
for (int i = 0; i < 50; i++) {
@@ -266,11 +266,11 @@ Test(arena_allocator, nested_structures_with_resize) {
allocator_reset(arena);
// Create array of int pointers (like dyn_array of dyn_arrays)
int **rows = make_arr(int*, arena);
int **rows = arr_make(int*, arena);
// Add 5 rows
for (int i = 0; i < 5; i++) {
int *row = make_arr(int, arena);
int *row = arr_make(int, arena);
for (int j = 0; j < 10; j++) {
arr_append(row, i * 10 + j);
}
+15 -15
View File
@@ -8,7 +8,7 @@ static bool int_eq(const void *a, const void *b) {
Test(dynamic_arrays, append) {
with_borrow(alloc) {
int *numbers = make_arr(int, alloc);
int *numbers = arr_make(int, alloc);
arr_append(numbers, 40);
arr_append(numbers, 41);
arr_append(numbers, 42);
@@ -28,7 +28,7 @@ Test(dynamic_arrays, append) {
Test(dynamic_arrays, pop) {
with_borrow(alloc) {
int *numbers = make_arr(int, alloc);
int *numbers = arr_make(int, alloc);
arr_append(numbers, 40);
arr_append(numbers, 41);
arr_append(numbers, 42);
@@ -55,7 +55,7 @@ Test(dynamic_arrays, pop) {
Test(dynamic_arrays, contains) {
with_borrow(alloc) {
int *numbers = make_arr(int, alloc);
int *numbers = arr_make(int, alloc);
arr_append(numbers, 20);
cr_expect(arr_contains(numbers, (int){20}));
arr_reset(numbers);
@@ -75,7 +75,7 @@ Test(dynamic_arrays, contains) {
Test(dynamic_arrays, contains_found) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc, .initial_capacity = 5);
int *arr = arr_make(int, alloc, .initial_capacity = 5);
arr_append(arr, 10);
arr_append(arr, 20);
arr_append(arr, 30);
@@ -88,7 +88,7 @@ Test(dynamic_arrays, contains_found) {
Test(dynamic_arrays, contains_not_found) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc, .initial_capacity = 5);
int *arr = arr_make(int, alloc, .initial_capacity = 5);
arr_append(arr, 10);
arr_append(arr, 20);
arr_append(arr, 30);
@@ -101,7 +101,7 @@ Test(dynamic_arrays, contains_not_found) {
Test(dynamic_arrays, contains_empty) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc, .initial_capacity = 5);
int *arr = arr_make(int, alloc, .initial_capacity = 5);
cr_assert_not(arr_contains(arr, (int){10}));
}
@@ -110,7 +110,7 @@ Test(dynamic_arrays, contains_empty) {
Test(dynamic_arrays, contains_cmp_found) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc, .initial_capacity = 5);
int *arr = arr_make(int, alloc, .initial_capacity = 5);
arr_append(arr, 10);
arr_append(arr, 20);
arr_append(arr, 30);
@@ -123,7 +123,7 @@ Test(dynamic_arrays, contains_cmp_found) {
Test(dynamic_arrays, contains_cmp_not_found) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc, .initial_capacity = 5);
int *arr = arr_make(int, alloc, .initial_capacity = 5);
arr_append(arr, 10);
arr_append(arr, 20);
arr_append(arr, 30);
@@ -136,7 +136,7 @@ Test(dynamic_arrays, contains_cmp_not_found) {
Test(dynamic_arrays, contains_cmp_empty) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc, .initial_capacity = 5);
int *arr = arr_make(int, alloc, .initial_capacity = 5);
cr_assert_not(arr_contains_cmp(arr, int_eq, (int){10}));
}
@@ -156,7 +156,7 @@ static bool kv_eq_by_key(const void *a, const void *b) {
Test(dynamic_arrays, contains_cmp_key_value_map) {
with_borrow(alloc) {
kv_pair_t *map = make_arr(kv_pair_t, alloc, .initial_capacity = 10);
kv_pair_t *map = arr_make(kv_pair_t, alloc, .initial_capacity = 10);
// Add some key-value pairs
arr_append(map, ((kv_pair_t){.key = 1, .value = "one"}));
@@ -176,7 +176,7 @@ Test(dynamic_arrays, contains_cmp_key_value_map) {
Test(dynamic_arrays, get_valid_indices) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc);
int *arr = arr_make(int, alloc);
arr_append(arr, 10);
arr_append(arr, 20);
arr_append(arr, 30);
@@ -191,7 +191,7 @@ Test(dynamic_arrays, get_valid_indices) {
Test(dynamic_arrays, set_valid_indices) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc);
int *arr = arr_make(int, alloc);
arr_append(arr, 10);
arr_append(arr, 20);
arr_append(arr, 30);
@@ -208,7 +208,7 @@ Test(dynamic_arrays, set_valid_indices) {
Test(dynamic_arrays, get_out_of_bounds, .exit_code = 1) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc);
int *arr = arr_make(int, alloc);
arr_append(arr, 10);
arr_append(arr, 20);
dyn_array_bounds_check_func( arr, 2, __FILE__, __LINE__, false);
@@ -217,7 +217,7 @@ Test(dynamic_arrays, get_out_of_bounds, .exit_code = 1) {
Test(dynamic_arrays, set_out_of_bounds, .exit_code = 1) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc);
int *arr = arr_make(int, alloc);
arr_append(arr, 10);
arr_append(arr, 20);
@@ -227,7 +227,7 @@ Test(dynamic_arrays, set_out_of_bounds, .exit_code = 1) {
Test(dynamic_arrays, get_empty_array, .exit_code = 1) {
with_borrow(alloc) {
int *arr = make_arr(int, alloc);
int *arr = arr_make(int, alloc);
dyn_array_bounds_check_func( arr, 0, __FILE__, __LINE__, false);
}
}
+34
View File
@@ -0,0 +1,34 @@
#include <criterion/criterion.h>
#include "cig.h"
#include <stdio.h>
#define FLAG_FREE (-1)
#define FLAG_TOMBSTONE (-2)
static int count_filled(void *map) {
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, map);
int count = 0;
for (unsigned int i = 0; i < header->mapping_capacity; i++) {
if (header->mapping_arr[i] >= 0) {
count++;
}
}
return count;
}
Test(map, test) {
with_borrow(allocator) {
int *s;
set_init(&s, allocator);
int count = count_filled((void*)s);
cr_assert_eq(count, 0, "%d != 0", count);
for (int i = 0; i < 10; i++) {
set_add(&s, 1);
count = count_filled((void*)s);
cr_assert_eq(count, 1, "%d != 0", count);
map_print_mapping_state((void*)s, stdout);
fflush(stdout);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
#include <stdbool.h>
#include <criterion/criterion.h>
#include "cig.h"
typedef long long T;
#define CAPACITY 50
Test(map_hash_key, foo) {
map_header_t header = (map_header_t){0};
header.key_size = sizeof(T);
header.mapping_capacity = CAPACITY;
bool hashed[CAPACITY] = {0};
for (T i = 0; i < CAPACITY; i++) {
uint32_t index = map_hash_key(&header, (const uint8_t*)(&i));
cr_assert(index < CAPACITY);
hashed[index] = true;
}
int n_unique = 0;
for (int i = 0; i < CAPACITY; i++) {
if (hashed[i]) {
n_unique++;
}
}
cr_assert_gt(n_unique, 1);
cr_assert_lt(n_unique, CAPACITY);
printf("Expect high number below 50. Got %d unique hashes.\nThis means %d collisions\n", n_unique, (int)(CAPACITY - n_unique));
}
// TODO: test hash with custom hash function.
+80
View File
@@ -0,0 +1,80 @@
#include <criterion/criterion.h>
#include "cig.h"
Test(map_key_equals, ints) {
{ // should be equal
int key1 = 20;
int key2 = 20;
map_header_t header = (map_header_t) { 0 };
header.key_size = sizeof(key1);
cr_assert(
map_key_equals(
&header,
(const uint8_t*)&key1,
(const uint8_t*)&key2
),
"key %d is not equal to key %d",
key1,
key2
);
}
{ // should not be equal
int key1 = 20;
int key2 = 21;
map_header_t header = (map_header_t) { 0 };
header.key_size = sizeof(key1);
cr_assert(
!map_key_equals(
&header,
(const uint8_t*)&key1,
(const uint8_t*)&key2
),
"key %d is equal to key %d",
key1,
key2
);
}
}
static bool inverse_int_equals(const void *key1, const void *key2) {
int key1_i = *((int*)key1);
int key2_i = *((int*)key2);
return key1_i != key2_i;
}
Test(map_key_equals, custom) {
{ // should be equal
int key1 = 20;
int key2 = 21;
map_header_t header = (map_header_t) { 0 };
header.key_size = sizeof(key1);
header.equals = inverse_int_equals;
cr_assert(
map_key_equals(
&header,
(const uint8_t*)&key1,
(const uint8_t*)&key2
),
"key %d is equal to key %d",
key1,
key2
);
}
{ // should not be equal
int key1 = 20;
int key2 = 20;
map_header_t header = (map_header_t) { 0 };
header.key_size = sizeof(key1);
header.equals = inverse_int_equals;
cr_assert(
!map_key_equals(
&header,
(const uint8_t*)&key1,
(const uint8_t*)&key2
),
"key %d is not equal to key %d",
key1,
key2
);
}
}
-43
View File
@@ -1,43 +0,0 @@
#include <assert.h>
#include <criterion/criterion.h>
#include "cig.h"
Test(scanner, looks_like_float) {
const char *buffer =
"1.0 true\n"
"12.34 true\n"
"0.5 true\n"
"9.99 true\n"
"+1.2 true\n"
"-3.14 true\n"
"1 false\n"
"42 false\n"
"+7 false\n"
"1. false\n"
".5 false\n"
"+. false\n"
"abc false\n"
"+x false\n";
with_borrow(allocator) {
scanner_t scanner = make_scanner("test", buffer, allocator);
while (!scan_eof(&scanner)) {
bool does_look_like_float = looks_like_float(&scanner);
bool does_look_like_int = looks_like_float(&scanner);
while (*scanner.cur++ != ' ');
bool expect;
if (scan_literal(&scanner, "true")) {
expect = true;
} else if (scan_literal(&scanner, "false")) {
expect = false;
} else {
assert(false && "invalid expectation");
}
scan_whitespace(&scanner);
cr_assert_eq(does_look_like_float, expect);
if (does_look_like_float) {
cr_assert(does_look_like_int);
}
}
}
}