Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a6f29f3a6 | |||
| 459af00c16 | |||
| ed9e69bc06 | |||
| 8be1c2254b | |||
| b2ba81c40a | |||
| e1bd55e470 | |||
| 5ea8d2ea32 | |||
| edcd8aaa1d | |||
| 6b36507a00 | |||
| 2fd707741b | |||
| 1c19b4435c | |||
| 7bbe0747c5 | |||
| 3b980dd4e4 | |||
| d0d98170ea | |||
| 5dcddd3474 | |||
| 6289039ed4 | |||
| a0e3fa8503 | |||
| 0a6e089581 | |||
| 7ddd7707db | |||
| 7d39b21c98 | |||
| 9bc0102501 | |||
| 64829d2ba2 | |||
| 81d7f6a067 | |||
| 1d6355a216 | |||
| ff5815887e | |||
| 8e6f44323e | |||
| 84f982329e | |||
| 9a07b00a2c | |||
| 83d66aaf36 | |||
| 6aa10199a5 | |||
| 96c25dc3ad | |||
| da4de1c560 | |||
| 253e095e4d | |||
| e41ef17d6b | |||
| e5331e8e81 | |||
| f0d7f0ecb6 | |||
| 9e99293c56 | |||
| 0d5a2b8115 | |||
| de5a5cd9c2 | |||
| d34fec63a0 | |||
| 70f9cef280 | |||
| ac4912abc5 | |||
| 9140019767 | |||
| 24dd5250f8 | |||
| 0f7f092ad3 | |||
| fbb340d465 | |||
| fdb5bb390c | |||
| 8fa5e8c0bf | |||
| f726956c78 | |||
| 0c50c5f0f7 | |||
| 95a1d8eeff | |||
| 941baafb01 |
@@ -1,2 +1,4 @@
|
||||
tags
|
||||
*.un~
|
||||
/foobar
|
||||
/foobar-expanded.c
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# what is the goal?
|
||||
- A standard library inspired by languages like zig, odin and rust.
|
||||
- Single header, multiple source files, and no build step.
|
||||
|
||||
|
||||
# current and coming features
|
||||
|
||||
- [x] allocators
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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}} .
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user