commit ec1b43354cef997f168f889d6830105e4ffcc292 from: Evan Burkey date: Sat Mar 14 19:15:01 2026 UTC fix ~60 bugs, add NULL checks, expand test coverage across all 14 modules commit - 1a06caead69676af14aec4f145173cd819caf17d commit + ec1b43354cef997f168f889d6830105e4ffcc292 blob - 82a7d72d0085fded034b6622d51eb0a1b5456546 blob + 5929800cc2f0c3b446496b3b877d8905ea0c6d2b --- .gitignore +++ .gitignore @@ -10,3 +10,4 @@ testrunner test_* .idea netmanual +CLAUDE.md blob - c26da16ce4292aed9068c75fc09281101633362a blob + 1c5b6b7488489a9b65510916e0c9bed3cf3a231b --- CMakeLists.txt +++ CMakeLists.txt @@ -57,6 +57,8 @@ if(${CMAKE_PROJECT_NAME} STREQUAL flint) crypto parsing memory + utility + input ) foreach(t ${TESTS}) blob - 5141822302a27a762d2adb8e63070a4c8f59207a blob + 990a6b4b4f40c572c5304d2b7b4bf11c32065a5e --- include/lfbinarytree.h +++ include/lfbinarytree.h @@ -1,6 +1,8 @@ #ifndef LIBFLINT_BINARY_TREE_H #define LIBFLINT_BINARY_TREE_H +#include + typedef struct BinTreeNode { void *data; struct BinTreeNode *left; @@ -10,13 +12,17 @@ typedef struct BinTreeNode { typedef struct { int size; - int (*compare)(const void *a, const void *b); - void (*destroy)(void *data); struct BinTreeNode *root; } BinTree; +enum { + BINTREE_PREORDER, + BINTREE_INORDER, + BINTREE_POSTORDER +}; + void bintree_init(BinTree *tree, void (*destroy)(void *data)); void bintree_destroy(BinTree *tree); @@ -29,10 +35,14 @@ void bintree_rem_left(BinTree *tree, BinTreeNode *node void bintree_rem_right(BinTree *tree, BinTreeNode *node); -//int bintree_merge(BinTree *merge, BinTree *left, BinTree *right, void *data); +int bintree_merge(BinTree *merge, BinTree *left, BinTree *right, void *data); -void bintree_debug_print(BinTree *tree); +void bintree_traverse(BinTree *tree, BinTreeNode *node, int order, + void (*visitor)(void *data)); +void bintree_debug_print(BinTree *tree, + void (*print_fn)(void *data, char *buf, size_t buf_sz)); + #define bintree_is_eob(node) ((node) == NULL) #define bintree_is_leaf(node) ((node)->left == NULL && (node)->right == NULL) blob - 116107cf7edb41ba8c57145587e606f5ac7cd369 blob + 95b459a7133a40b484189cf4ccb3af097805d71c --- include/lfcrypto.h +++ include/lfcrypto.h @@ -13,7 +13,7 @@ char *hex_to_str(const unsigned char *hex, size_t sz); unsigned char* repeating_key_xor(const unsigned char* s, size_t s_sz, const unsigned char* key, size_t k_sz); unsigned char *repeating_key_xor_s(const char* s, const char* key); -unsigned int hamming_distance_s(const char *a, const char *b); -unsigned int hamming_distance(unsigned char *a, unsigned char *b, size_t sz); +int hamming_distance_s(const char *a, const char *b); +int hamming_distance(const unsigned char *a, const unsigned char *b, size_t sz); #endif // LIBFLINT_CRYPTO_H blob - 3e5bc239140cb52650fb5c582e14275216e684a3 blob + 3fd88e9c777b1603f9e8a9b252d2662d1b3c38c7 --- include/lfinput.h +++ include/lfinput.h @@ -3,22 +3,22 @@ #include -unsigned char *get_binary(const char *, size_t *fsz); +unsigned char *inp_get_binary(const char *, size_t *fsz); -char *get_input(const char *); +char *inp_get_input(const char *); -char **split(char *, size_t *, const char *); +char **inp_split(char *, size_t *, const char *); -char **get_lines(const char *, size_t *); +char **inp_get_lines(const char *, size_t *); -int *get_ints(const char *, size_t *); +int *inp_get_ints(const char *, size_t *); -void del_split(char **); +void inp_del_split(char **); -void del_lines(char **); +void inp_del_lines(char **); #define DEFAULT_CAPTURE_SYSTEM_BUFSIZE 1024 -const char *capture_system(const char *cmd, int buf_sz); +char *inp_capture_system(const char *cmd, size_t buf_sz); #endif // LIBFLINT_INPUT_H blob - 6c63e57c408b7f62baa2bed580e9fa6b81c19e00 blob + a7e55bd3ed794ace7ba3394309b78fc3c49e9f93 --- include/lflinkedlist.h +++ include/lflinkedlist.h @@ -20,7 +20,8 @@ typedef struct { struct ListNode *tail; } List; -void ll_init(List *list, void (*destroy)(void *data)); +void ll_init(List *list, void (*destroy)(void *data), + int (*match)(const void *a, const void *b)); void ll_destroy(List *list); @@ -34,13 +35,13 @@ int ll_remove_next(List *list, ListNode *node, void ** int ll_remove_prev(List *list, ListNode *node, void **data); -ListNode *ll_find(List *list, const void *data); +ListNode *ll_find(const List *list, const void *data); void ll_reverse(List *list); void ll_sort(List *list, int (*cmp)(const void *a, const void *b)); -void **ll_to_array(List *list); +void **ll_to_array(const List *list); /* Provides ListNode *node for the iteration loop */ #define LL_ITER(list) for(ListNode *node = (list)->head; node != NULL; node = node->next) blob - 4dde872bd863159c446a310946045764595ac6c6 blob + 77e756ed98a238fd981a346aae8fcb8baa780a4a --- include/lfmacos.h +++ include/lfmacos.h @@ -3,6 +3,7 @@ #if defined(__APPLE__) || defined(__MACH__) +#include #include typedef struct { @@ -19,6 +20,7 @@ typedef struct { } ProcessData; ProcessData *new_ProcessData(); +void destroy_ProcessData(ProcessData *pd); int update_process(pid_t pid, ProcessData *proc); void *reallocarray(void *optr, size_t nmemb, size_t size); blob - 5267d993e8cabdd120bb33ff40697062085caf05 blob + 498c4fb3364edcf2c35928df63fa8685c10d13a0 --- include/lfmath.h +++ include/lfmath.h @@ -5,6 +5,8 @@ #include "lfutility.h" +int abs_int(int a); + int max_int(int a, int b); int min_int(int a, int b); @@ -13,8 +15,6 @@ int clamp_int(int i, int low, int high); int binstr_to_int(const char *s); -int abs_int(int i); - int is_power_of_two(int i); int gcd(int a, int b); blob - a1a15aebc088f0eccd463e74c523616e8a1a64d6 blob + 574d29a129a4809bbdc23f1b0d4adaf645caae65 --- include/lfmemory.h +++ include/lfmemory.h @@ -23,24 +23,25 @@ typedef struct { size_t save_count; } ArenaAllocator; -void arena_init(ArenaAllocator *allocator, size_t buf_sz); +int arena_init(ArenaAllocator *allocator, size_t buf_sz); void arena_free(ArenaAllocator *allocator); void *arena_malloc(ArenaAllocator* allocator, size_t size); -void arena_resize_buf(ArenaAllocator *allocator, size_t new_sz); +int arena_resize_buf(ArenaAllocator *allocator, const size_t new_sz); void *arena_resize(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz); void arena_clear(ArenaAllocator *allocator); int arena_save(ArenaAllocator *allocator); void arena_restore(ArenaAllocator *allocator); typedef struct { - unsigned char *buf; + unsigned char *buf; size_t buf_sz; size_t chunk_size; + size_t aligned_start; List *free_list; } PoolAllocator; -void pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align); +int pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align); void pool_free(PoolAllocator *allocator, void *ptr); void pool_free_all(PoolAllocator *allocator); void *pool_alloc(PoolAllocator *allocator); blob - 2cda586c07b5cccd7e27814fc6002bef118f21f3 blob + 328826df46992f5535d9323a8f63e1cec6afe61d --- include/lfqueue.h +++ include/lfqueue.h @@ -4,12 +4,14 @@ #include "lflinkedlist.h" #define Queue List +#define queue_size(q) ((q)->size) +#define queue_is_empty(q) ((q)->size == 0) void queue_init(Queue *queue, void (*destroy)(void *data)); void queue_destroy(Queue *queue); -int queue_enqueue(Queue *queue, void *data); +int queue_enqueue(Queue *queue, const void *data); int queue_dequeue(Queue *queue, void **data); blob - b2c240f8a7546642af828c74086afbb91d4fa8f9 blob + 5ac9dc2b321fb8c32912d5d29a505e0c842e5349 --- include/lfset.h +++ include/lfset.h @@ -4,12 +4,14 @@ #include "lflinkedlist.h" #define Set List +#define set_size(s) ((s)->size) void set_init(Set *set, int (*match)(const void *a, const void *b), void (*destroy)(void *data)); void set_destroy(Set *set); +/* Returns 0 on success, 1 if data is a duplicate, -1 on error */ int set_insert(Set *set, const void *data); int set_remove(Set *set, void **data); blob - 6f0739d385fe5b777b1296e05674f7d38beba69a blob + 529493a95ebb78a3ef4c9a172475dabd2b08fb54 --- include/lfstack.h +++ include/lfstack.h @@ -4,6 +4,8 @@ #include "lflinkedlist.h" #define Stack List +#define stack_size(s) ((s)->size) +#define stack_is_empty(s) ((s)->size == 0) void stack_init(Stack *stack, void (*destroy)(void *data)); blob - 1e49819533239f64b0c2f0d005cdc4802c9cfd2d blob + 7c37ca946b57b7d24bdb27a1948635e6866e069d --- include/lfstring.h +++ include/lfstring.h @@ -3,9 +3,9 @@ #include -int find_substrings(const char* haystack, const char* needle, size_t *num_substrings, size_t **substrings); +int str_find(const char* haystack, const char* needle, size_t *num_substrings, size_t **substrings); -char* substr(const char* str, size_t idx, size_t len); +char* str_substr(const char* str, size_t idx, size_t len); char *str_trim(const char *str); @@ -21,4 +21,8 @@ char *str_to_upper(const char *str); char *str_to_lower(const char *str); +char **str_split(const char *str, const char *delim, int *count); + +int str_contains(const char *str, const char *substr); + #endif // LIBFLINT_H_STRING blob - 9cf55b9abff79b53ae28c4118350e650b983fbd0 blob + 6278e8bb576be1f170d997739bf1e2d68ce76100 --- include/lfutility.h +++ include/lfutility.h @@ -8,7 +8,8 @@ typedef struct Point { Point Point_new(int x, int y); Point *Point_new_p(int x, int y); -int Point_cmp(Point a, Point b); +void Point_destroy(Point *p); +int Point_eq(const Point a, const Point b); int Point_cmp_p(const Point *a, const Point *b); int Point_cmp_v(const void *a, const void *b); blob - ba38adb6678861381669e0b59ea2d874f47eb8ad blob + 5a9eb12170d7652d176781cd41685ffa0547a466 --- include/lfvector.h +++ include/lfvector.h @@ -1,7 +1,7 @@ #ifndef LIBFLINT_H_VECTOR #define LIBFLINT_H_VECTOR -#include +#include typedef struct Vector { size_t capacity; @@ -14,7 +14,7 @@ int vec_init(Vector *vec, void (*destroy)(void *data)) int vec_init_with_capacity(Vector *vec, void (*destroy)(void *data), size_t cap); -void vec_clear(Vector *vec); +int vec_clear(Vector *vec); void vec_destroy(Vector *vec); blob - 037cd2638c2f251bc8df208e644d4732c6b0fe71 blob + c1e9ea6c0a08802cf5cb014032f12ccd1906899a --- src/binarytree.c +++ src/binarytree.c @@ -7,7 +7,6 @@ void bintree_init(BinTree *tree, void (*destroy)(void *data)) { tree->size = 0; tree->destroy = destroy; - tree->compare = NULL; tree->root = NULL; } @@ -22,7 +21,7 @@ int bintree_ins_left(BinTree *tree, BinTreeNode *node, if (node == NULL) { if (tree->size > 0) { - return 1; + return -1; } pos = &tree->root; } else { @@ -33,7 +32,7 @@ int bintree_ins_left(BinTree *tree, BinTreeNode *node, } if ((new_node = malloc(sizeof(BinTreeNode))) == NULL) { - return 2; + return -1; } new_node->data = data; @@ -51,7 +50,7 @@ int bintree_ins_right(BinTree *tree, BinTreeNode *node if (node == NULL) { if (tree->size > 0) { - return 1; + return -1; } pos = &tree->root; } else { @@ -62,7 +61,7 @@ int bintree_ins_right(BinTree *tree, BinTreeNode *node } if ((new_node = malloc(sizeof(BinTreeNode))) == NULL) { - return 2; + return -1; } new_node->data = data; @@ -123,6 +122,10 @@ void bintree_rem_right(BinTree *tree, BinTreeNode *nod } int bintree_merge(BinTree *merge, BinTree *left, BinTree *right, void *data) { + if (left == right) { + return -1; + } + bintree_init(merge, left->destroy); if (bintree_ins_left(merge, NULL, data) != 0) { bintree_destroy(merge); @@ -135,30 +138,63 @@ int bintree_merge(BinTree *merge, BinTree *left, BinTr left->root = NULL; left->size = 0; + + /* Preserve right's destroy callback before clearing */ right->root = NULL; right->size = 0; return 0; } -void print_node(char *prefix, BinTreeNode *node, int is_left, void (*pfunc)(void *data)) { +void bintree_traverse(BinTree *tree, BinTreeNode *node, int order, + void (*visitor)(void *data)) { + (void)tree; + if (node == NULL) { + return; + } + + switch (order) { + case BINTREE_PREORDER: + visitor(node->data); + bintree_traverse(tree, node->left, order, visitor); + bintree_traverse(tree, node->right, order, visitor); + break; + case BINTREE_INORDER: + bintree_traverse(tree, node->left, order, visitor); + visitor(node->data); + bintree_traverse(tree, node->right, order, visitor); + break; + case BINTREE_POSTORDER: + bintree_traverse(tree, node->left, order, visitor); + bintree_traverse(tree, node->right, order, visitor); + visitor(node->data); + break; + } +} + +static void print_node(char *prefix, BinTreeNode *node, int is_left, + void (*print_fn)(void *data, char *buf, size_t buf_sz)) { if (node != NULL) { - printf("%s%s", prefix, (is_left ? "├──" : "└──")); - pfunc(node->data); - char new_prefix[64]; - memset(new_prefix, 0, 64); - strlcat(new_prefix, prefix, 64); - strlcat(new_prefix, (is_left == 1 ? "│ " : " "), 64 - strlen(prefix)); - print_node(new_prefix, node->left, 1, pfunc); - print_node(new_prefix, node->right, 0, pfunc); + char label[64]; + print_fn(node->data, label, sizeof(label)); + printf("%s%s%s\n", prefix, (is_left ? "├──" : "└──"), label); + + char new_prefix[256]; + snprintf(new_prefix, sizeof(new_prefix), "%s%s", + prefix, (is_left ? "│ " : " ")); + print_node(new_prefix, node->left, 1, print_fn); + print_node(new_prefix, node->right, 0, print_fn); } } -void bintree_debug_pfunc_int(void *data) { - int i = *((int *) data); - printf("%d\n", i); +static void default_print_int(void *data, char *buf, size_t buf_sz) { + snprintf(buf, buf_sz, "%d", *(int *)data); } -void bintree_debug_print(BinTree *tree) { - print_node("", tree->root, 0, bintree_debug_pfunc_int); +void bintree_debug_print(BinTree *tree, + void (*print_fn)(void *data, char *buf, size_t buf_sz)) { + if (print_fn == NULL) { + print_fn = default_print_int; + } + print_node("", tree->root, 0, print_fn); } blob - 7dc6dc22e56c5d9d88b4e00d81e93377619c0ffc blob + 8139381294e40e86fb576e7cea57329dd738e9eb --- src/crypto.c +++ src/crypto.c @@ -203,6 +203,10 @@ unsigned char *b64_decode(const char *s, size_t sz, si } unsigned char *hex_decode(const char *orig, size_t *sz) { + if (sz == NULL) { + return NULL; + } + size_t buf_sz = strlen(orig) + 1; const char *sptr = orig; if (strncmp(orig, "0x", 2) == 0) { @@ -215,16 +219,25 @@ unsigned char *hex_decode(const char *orig, size_t *sz } char *buf = malloc(sizeof(char) * buf_sz); + if (buf == NULL) { + return NULL; + } + if (strlen(sptr) % 2 != 0) { - strlcpy(buf + 1, sptr, buf_sz - 1); + memcpy(buf + 1, sptr, strlen(sptr)); buf[0] = '0'; } else { - strlcpy(buf, sptr, buf_sz); + memcpy(buf, sptr, strlen(sptr)); } buf[buf_sz - 1] = '\0'; *sz = buf_sz / 2; unsigned char *hex = malloc(sizeof(unsigned char) * *sz); + if (hex == NULL) { + free(buf); + return NULL; + } + const char *pos = buf; for (size_t i = 0; i < *sz; ++i) { @@ -239,6 +252,10 @@ unsigned char *hex_decode(const char *orig, size_t *sz char *hex_encode(const unsigned char *hex, size_t sz) { size_t ssz = sz * 2 + 1; char *s = malloc(sizeof(char) * ssz); + if (s == NULL) { + return NULL; + } + char *pos = s; for (size_t i = 0; i < sz; ++i) { @@ -252,6 +269,10 @@ char *hex_encode(const unsigned char *hex, size_t sz) char *hex_to_str(const unsigned char *hex, size_t sz) { char *s = malloc(sizeof(char) * (sz + 1)); + if (s == NULL) { + return NULL; + } + for (size_t i = 0; i < sz; ++i) { s[i] = (char)hex[i]; } @@ -260,7 +281,15 @@ char *hex_to_str(const unsigned char *hex, size_t sz) } unsigned char* repeating_key_xor(const unsigned char* s, size_t s_sz, const unsigned char* key, size_t k_sz) { + if (k_sz == 0) { + return NULL; + } + unsigned char* r = malloc(sizeof(unsigned char) * s_sz); + if (r == NULL) { + return NULL; + } + for (size_t i = 0, j = 0; i < s_sz; ++i) { r[i] = s[i] ^ key[j]; j = (j + 1) % k_sz; @@ -272,23 +301,23 @@ unsigned char *repeating_key_xor_s(const char* s, cons return repeating_key_xor((unsigned char*)s, strlen(s), (unsigned char*)key, strlen(key)); } -unsigned int hamming_distance_s(const char *a, const char *b) { +int hamming_distance_s(const char *a, const char *b) { size_t sz = strlen(a); if (sz != strlen(b)) { return -1; } - return hamming_distance((unsigned char *)a, (unsigned char *)b, sz); + return hamming_distance((const unsigned char *)a, (const unsigned char *)b, sz); } -unsigned int hamming_distance(unsigned char *a, unsigned char *b, size_t sz) { - unsigned int hamming = 0; +int hamming_distance(const unsigned char *a, const unsigned char *b, size_t sz) { + int hamming = 0; for (size_t i = 0; i < sz; ++i) { if (a[i] == b[i]) { continue; } unsigned char c = a[i] ^ b[i]; - unsigned int count = 0; + int count = 0; for (; c; count++) { c &= c - 1; } blob - dfb3e5a33f8c3184643b3c716537b25f2949adcc blob + c90bf7dfe7be8484d7200668e9a862d2f7357ffd --- src/input.c +++ src/input.c @@ -12,23 +12,29 @@ #include "lfinput.h" -static FILE* open_file(const char *path, size_t *fsz) { +static FILE* inp_open_file(const char *path, size_t *fsz, const char *mode) { FILE *fp = NULL; - fp = fopen(path, "r"); + fp = fopen(path, mode); if (fp == NULL) { fprintf(stderr, "Failed to open %s. Returning NULL\n", path); return NULL; } fseek(fp, 0, SEEK_END); - *fsz = ftell(fp); + long pos = ftell(fp); + if (pos < 0) { + fprintf(stderr, "ftell failed for %s. Returning NULL\n", path); + fclose(fp); + return NULL; + } + *fsz = (size_t)pos; rewind(fp); return fp; } -unsigned char *get_binary(const char *path, size_t *fsz) { - FILE *fp = open_file(path, fsz); +unsigned char *inp_get_binary(const char *path, size_t *fsz) { + FILE *fp = inp_open_file(path, fsz, "rb"); if (fp == NULL) { return NULL; } @@ -47,9 +53,9 @@ unsigned char *get_binary(const char *path, size_t *fs return buf; } -char *get_input(const char *path) { +char *inp_get_input(const char *path) { size_t fsz = 0; - FILE *fp = open_file(path, &fsz); + FILE *fp = inp_open_file(path, &fsz, "r"); if (fp == NULL) { return NULL; } @@ -69,18 +75,24 @@ char *get_input(const char *path) { return buf; } -char **split(char *s, size_t *lsz, const char *delim) { +char **inp_split(char *s, size_t *lsz, const char *delim) { + if (s == NULL || lsz == NULL || delim == NULL) { + return NULL; + } + char **lines = NULL; char *t = strtok(s, delim); size_t n = 0; while (t != NULL) { - lines = realloc(lines, sizeof(char *) * ++n); - if (lines == NULL) { + char **tmp = realloc(lines, sizeof(char *) * (n + 1)); + if (tmp == NULL) { fprintf(stderr, "Failed to realloc lines buffer. Returning NULL\n"); - free(s); + free(lines); return NULL; } + lines = tmp; + n++; lines[n - 1] = t; t = strtok(NULL, delim); } @@ -89,13 +101,75 @@ char **split(char *s, size_t *lsz, const char *delim) return lines; } -char **get_lines(const char *path, size_t *lsz) { - return split(get_input(path), lsz, "\n"); +/* + * Internal split used by inp_get_lines. Stores the original string pointer + * at index [-1] (one slot before the returned array) so inp_del_lines can + * free the correct base even when strtok skips leading delimiters. + */ +static char **split_lines(char *s, size_t *lsz, const char *delim) { + if (s == NULL || lsz == NULL || delim == NULL) { + return NULL; + } + + char *base = s; + /* Start with space for the hidden base slot + 1 token */ + size_t n = 0; + size_t cap = 2; + char **raw = malloc(sizeof(char *) * cap); + if (raw == NULL) { + free(base); + return NULL; + } + raw[0] = base; /* hidden slot stores original allocation */ + + char *t = strtok(s, delim); + while (t != NULL) { + n++; + if (n + 1 >= cap) { + cap *= 2; + char **tmp = realloc(raw, sizeof(char *) * cap); + if (tmp == NULL) { + free(raw); + free(base); + *lsz = 0; + return NULL; + } + raw = tmp; + } + raw[n] = t; + t = strtok(NULL, delim); + } + + if (n == 0) { + free(base); + free(raw); + *lsz = 0; + return NULL; + } + + *lsz = n; + return raw + 1; /* return pointer past the hidden base slot */ } -int *get_ints(const char *path, size_t *sz) { - char **lines = get_lines(path, sz); +char **inp_get_lines(const char *path, size_t *lsz) { + return split_lines(inp_get_input(path), lsz, "\n"); +} + +int *inp_get_ints(const char *path, size_t *sz) { + if (path == NULL || sz == NULL) { + return NULL; + } + + char **lines = inp_get_lines(path, sz); + if (lines == NULL) { + return NULL; + } + int *i = malloc(sizeof(int) * *sz); + if (i == NULL) { + inp_del_lines(lines); + return NULL; + } for (size_t idx = 0; idx < *sz; idx++) { int n; @@ -104,37 +178,71 @@ int *get_ints(const char *path, size_t *sz) { if (errstr) { fprintf(stderr, "Failed to convert %s to int. Returning NULL\n", lines[idx]); free(i); - del_lines(lines); + inp_del_lines(lines); return NULL; } i[idx] = n; } - del_lines(lines); + inp_del_lines(lines); return i; } -void del_split(char **sp) { - free(sp[0]); +void inp_del_split(char **sp) { + if (sp == NULL) { + return; + } free(sp); } -void del_lines(char **lines) { - del_split(lines); +void inp_del_lines(char **lines) { + if (lines == NULL) { + return; + } + /* lines[-1] holds the original string base pointer (set by split_lines) */ + free(lines[-1]); + free(lines - 1); } -const char *capture_system(const char *cmd, int buf_sz) { +char *inp_capture_system(const char *cmd, size_t buf_sz) { + if (cmd == NULL) { + return NULL; + } + if (buf_sz == 0) { buf_sz = DEFAULT_CAPTURE_SYSTEM_BUFSIZE; } - char *buf = malloc(buf_sz); + FILE *tmp = popen(cmd, "r"); if (tmp == NULL) { fprintf(stderr, "libflint: failed to open FILE *tmp in capture_system. Errno: %d\n", errno); - free(buf); return NULL; } - fgets(buf, buf_sz, tmp); + + size_t total = 0; + size_t cap = buf_sz; + char *buf = malloc(cap); + if (buf == NULL) { + pclose(tmp); + return NULL; + } + + size_t nread; + while ((nread = fread(buf + total, 1, cap - total - 1, tmp)) > 0) { + total += nread; + if (total + 1 >= cap) { + cap *= 2; + char *tmp2 = realloc(buf, cap); + if (tmp2 == NULL) { + free(buf); + pclose(tmp); + return NULL; + } + buf = tmp2; + } + } + + buf[total] = '\0'; pclose(tmp); return buf; -} \ No newline at end of file +} blob - 3efce6794774833d7c2752b2fe570b9e354da7dd blob + 5caa87bc58c50c7faf32f28982bb3a2ca98399b9 --- src/linkedlist.c +++ src/linkedlist.c @@ -3,9 +3,11 @@ #include "lflinkedlist.h" -void ll_init(List *list, void (*destroy)(void *data)) { +void ll_init(List *list, void (*destroy)(void *data), + int (*match)(const void *a, const void *b)) { list->size = 0; list->destroy = destroy; + list->match = match; list->head = NULL; list->tail = NULL; } @@ -81,7 +83,7 @@ int ll_ins_prev(List *list, ListNode *node, const void } int ll_remove(List *list, ListNode *node, void **data) { - if (node == NULL || list->size == 0) { + if (node == NULL || data == NULL || list->size == 0) { return -1; } @@ -120,14 +122,22 @@ int ll_remove_prev(List *list, ListNode *node, void ** return ll_remove(list, node->prev, data); } -ListNode *ll_find(List *list, const void *data) { - if (list->match == NULL) { +ListNode *ll_find(const List *list, const void *data) { + if (list == NULL) { return NULL; } - for (ListNode *node = list->head; node != NULL; node = node->next) { - if (list->match(data, node->data)) { - return node; + ListNode *node = list->head; + while (node != NULL) { + if (list->match != NULL) { + if (list->match(node->data, data) == 0) { + return node; + } + } else { + if (node->data == data) { + return node; + } } + node = node->next; } return NULL; } @@ -212,8 +222,8 @@ void ll_sort(List *list, int (*cmp)(const void *a, con list->tail = node; } -void **ll_to_array(List *list) { - if (list->size == 0) { +void **ll_to_array(const List *list) { + if (list == NULL || list->size == 0) { return NULL; } void **arr = malloc(list->size * sizeof(void *)); blob - 3bbcf1c246a5b5475ef1fb51e62831f676c6dcf6 blob + 3409cf0851cd610e64365c0e575fc0044547bf0f --- src/macos.c +++ src/macos.c @@ -1,3 +1,5 @@ +#if defined(__APPLE__) || defined(__MACH__) + #include #include #include @@ -10,20 +12,29 @@ #define NEW_PROCESS_SENTINEL (-1.0) ProcessData *new_ProcessData() { - ProcessData *pd = malloc(sizeof(ProcessData)); + ProcessData *pd = calloc(1, sizeof(ProcessData)); + if (!pd) return NULL; pd->last_total_consumed = NEW_PROCESS_SENTINEL; return pd; } +void destroy_ProcessData(ProcessData *pd) { + free(pd); +} + int update_process(pid_t pid, ProcessData *proc) { struct proc_taskinfo taskinfo; const int r = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskinfo, PROC_PIDTASKINFO_SIZE); if (r != PROC_PIDTASKINFO_SIZE) { - return 1; + return -1; } - mach_timebase_info_data_t info; - mach_timebase_info(&info); + static mach_timebase_info_data_t info; + static int info_init = 0; + if (!info_init) { + mach_timebase_info(&info); + info_init = 1; + } const double ns_per_tick = (double)info.numer / (double)info.denom; time(&(proc->timestamp)); @@ -34,7 +45,11 @@ int update_process(pid_t pid, ProcessData *proc) { if (proc->last_total_consumed != NEW_PROCESS_SENTINEL) { time_t t = proc->timestamp - proc->last_timestamp; - proc->percent_cpu = 100.0 * (proc->total_user_time + proc->total_kernel_time - proc->last_total_consumed) / t; + if (t > 0) { + proc->percent_cpu = 100.0 * (proc->total_user_time + proc->total_kernel_time - proc->last_total_consumed) / t; + } else { + proc->percent_cpu = 0.0; + } } else { proc->percent_cpu = 0.0; } @@ -80,3 +95,5 @@ void *reallocarray(void *optr, size_t nmemb, size_t si } return realloc(optr, size * nmemb); } + +#endif /* defined(__APPLE__) || defined(__MACH__) */ blob - 08fed2d2db0c250cc94df7548df5cff989f536f2 blob + efcb9566eda70adaf457b15114aeb99bb658a2c6 --- src/math.c +++ src/math.c @@ -1,8 +1,16 @@ +#include #include #include #include "lfmath.h" +int abs_int(int a) { + if (a == INT_MIN) { + return INT_MAX; + } + return a < 0 ? -a : a; +} + int max_int(int a, int b) { if (a > b) { return a; @@ -18,6 +26,11 @@ int min_int(int a, int b) { } int clamp_int(int i, int low, int high) { + if (low > high) { + int tmp = low; + low = high; + high = tmp; + } if (i > high) { return high; } else if (i < low) { @@ -27,6 +40,9 @@ int clamp_int(int i, int low, int high) { } int binstr_to_int(const char *s) { + if (!s) { + return -1; + } int n = 0, m = 1; for (int i = (int) strlen(s) - 1; i >= 0; --i) { if (s[i] == '_') { @@ -41,19 +57,26 @@ int binstr_to_int(const char *s) { } Point *bresenham(int x0, int y0, int x1, int y1, size_t *sz) { - Point *line = NULL; - size_t n = 0; *sz = 0; - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int dx = abs(x1 - x0); + int dy = abs(y1 - y0); + size_t npts = (size_t)(dx > dy ? dx : dy) + 1; + + Point *line = malloc(sizeof(Point) * npts); + if (!line) { + return NULL; + } + + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; int err = (dx > dy ? dx : -dy) / 2, e2; + size_t n = 0; for (;;) { - line = realloc(line, sizeof(Point) * ++n); - ++*sz; - line[n - 1].x = x0; - line[n - 1].y = y0; + line[n].x = x0; + line[n].y = y0; + n++; if (x0 == x1 && y0 == y1) { break; @@ -70,6 +93,7 @@ Point *bresenham(int x0, int y0, int x1, int y1, size_ } } + *sz = n; return line; } @@ -77,10 +101,6 @@ Point *bresenham_p(Point p1, Point p2, size_t *sz) { return bresenham(p1.x, p1.y, p2.x, p2.y, sz); } -int abs_int(int i) { - return i < 0 ? -i : i; -} - int is_power_of_two(int i) { return i > 0 && (i & (i - 1)) == 0; } blob - 627dbf52c50beebcc40de79d911dd68f39120235 blob + f3385917f6f35f40d939609e8e961ca55cdbed16 --- src/memory.c +++ src/memory.c @@ -5,18 +5,20 @@ #include "lfmemory.h" -#define arena_sz(a) (a)->buf_sz - -void arena_init(ArenaAllocator *allocator, size_t buf_sz) { +int arena_init(ArenaAllocator *allocator, size_t buf_sz) { if (allocator == NULL) { - return; + return -1; } allocator->buf = malloc(sizeof(unsigned char) * buf_sz); + if (allocator->buf == NULL) { + return -1; + } allocator->buf_sz = buf_sz; allocator->offset_cur = 0; allocator->offset_prev = 0; allocator->save_count = 0; + return 0; } void arena_free(ArenaAllocator *allocator) { @@ -119,13 +121,17 @@ static void *arena_resize_align(ArenaAllocator *alloca return NULL; } -void arena_resize_buf(ArenaAllocator *allocator, const size_t new_sz) { +int arena_resize_buf(ArenaAllocator *allocator, const size_t new_sz) { + if (new_sz < allocator->offset_cur) { + return -1; + } unsigned char *new_buf = realloc(allocator->buf, sizeof(unsigned char) * new_sz); if (new_buf == NULL) { - return; + return -1; } allocator->buf = new_buf; allocator->buf_sz = new_sz; + return 0; } void *arena_malloc(ArenaAllocator *allocator, const size_t size) { @@ -136,33 +142,44 @@ void *arena_resize(ArenaAllocator *allocator, void *me return arena_resize_align(allocator, mem, old_sz, new_sz, LF_DEFAULT_ALIGNMENT); } -void pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align) { +int pool_init(PoolAllocator *allocator, size_t buf_sz, size_t chunk_sz, size_t chunk_align) { if (allocator == NULL) { - return; + return -1; } allocator->buf = malloc(sizeof(unsigned char) * buf_sz); + if (allocator->buf == NULL) { + return -1; + } uintptr_t istart = (uintptr_t)allocator->buf; uintptr_t start = align_forward(istart, chunk_align); - allocator->buf_sz = buf_sz - (start - istart); + allocator->aligned_start = (size_t)(start - istart); + allocator->buf_sz = buf_sz - allocator->aligned_start; allocator->chunk_size = align_forward(chunk_sz, chunk_align); if (allocator->chunk_size < sizeof(void *) || allocator->buf_sz < allocator->chunk_size) { free(allocator->buf); allocator->buf = NULL; allocator->buf_sz = 0; - return; + return -1; } allocator->free_list = malloc(sizeof(List)); - ll_init(allocator->free_list, NULL); + if (allocator->free_list == NULL) { + free(allocator->buf); + allocator->buf = NULL; + allocator->buf_sz = 0; + return -1; + } + ll_init(allocator->free_list, NULL, NULL); pool_free_all(allocator); + return 0; } void pool_free(PoolAllocator *allocator, void *ptr) { - const void *start = allocator->buf; - const void *end = &allocator->buf[allocator->buf_sz]; + const void *start = allocator->buf + allocator->aligned_start; + const void *end = allocator->buf + allocator->aligned_start + allocator->buf_sz; if (ptr == NULL) { return; @@ -177,11 +194,12 @@ void pool_free(PoolAllocator *allocator, void *ptr) { void pool_free_all(PoolAllocator *allocator) { ll_destroy(allocator->free_list); - ll_init(allocator->free_list, NULL); + ll_init(allocator->free_list, NULL, NULL); size_t chunk_count = allocator->buf_sz / allocator->chunk_size; for (size_t i = 0; i < chunk_count; ++i) { - ll_ins_next(allocator->free_list, allocator->free_list->head, &allocator->buf[i * allocator->chunk_size]); + ll_ins_next(allocator->free_list, allocator->free_list->head, + &allocator->buf[allocator->aligned_start + i * allocator->chunk_size]); } } @@ -203,5 +221,7 @@ void pool_destroy(PoolAllocator *allocator) { ll_destroy(allocator->free_list); free(allocator->free_list); free(allocator->buf); - free(allocator); + allocator->buf = NULL; + allocator->free_list = NULL; + allocator->buf_sz = 0; } blob - b4f2ebfa3ef9b4d8dd0f24d89f8d435b77ab21c0 blob + fb880b8851bdf1114fda35c721753926cf02e1ad --- src/network.c +++ src/network.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,9 @@ Server *new_server(ServerType type, const char *port, continue; } + int yes = 1; + setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + if (bind(s->fd, p->ai_addr, p->ai_addrlen) != 0) { close(s->fd); continue; @@ -70,6 +74,7 @@ Server *new_server(ServerType type, const char *port, if (p == NULL) { fprintf(stderr, "Failed to bind\n"); + freeaddrinfo(addr); free(s); return NULL; } @@ -79,8 +84,9 @@ Server *new_server(ServerType type, const char *port, } void delete_server(Server *s) { + if (s == NULL) return; + close(s->fd); free(s); - s = NULL; } int serve(Server *s, int backlog_size) { @@ -88,8 +94,10 @@ int serve(Server *s, int backlog_size) { backlog_size = DEFAULT_BACKLOG; } - if (listen(s->fd, backlog_size) != 0) { - return 1; + if (s->server_type != SERVERTYPE_UDP) { + if (listen(s->fd, backlog_size) != 0) { + return -1; + } } // Linux doesn't handle SA_RESTART properly, and I don't know about Windows (nor do I care) @@ -101,7 +109,7 @@ int serve(Server *s, int backlog_size) { sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL) == -1) { fprintf(stderr, "Failed to set sigaction\n"); - return 1; + return -1; } #endif @@ -111,9 +119,9 @@ int serve(Server *s, int backlog_size) { } static void *tcp_echo_thread(void *vargp) { + int fd = (int)(intptr_t)vargp; while (1) { char recv_buf[256]; - int fd = *(int *) vargp; int r = (int)recv(fd, recv_buf, 256, 0); if (r < 1) { @@ -147,6 +155,7 @@ void handler_tcp_echo(Server *s) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&srv_tid, &attr, tcp_echo_thread, &new_fd); + pthread_create(&srv_tid, &attr, tcp_echo_thread, (void *)(intptr_t)new_fd); + pthread_attr_destroy(&attr); } } blob - 5fa7f48c7a3818d6b9211a70803e00e0572e2362 blob + 18a81d0e82958063db5ea097fa554dbb7df9ea02 --- src/parsing.c +++ src/parsing.c @@ -1,3 +1,4 @@ +#include #include "lfparsing.h" static int ses_score_sw(char c) { @@ -20,6 +21,9 @@ static int ses_score_sw(char c) { } int simple_english_scoring(const char *s) { + if (s == NULL) { + return -1; + } int score = 0; for (const char *c = s; *c != '\0'; ++c) { score += ses_score_sw(*c); blob - c591d62d376d9c307faf43a8ab13cdf29fe75098 blob + 264690c25a6336bdd59acf253c19fff5db139a6a --- src/queue.c +++ src/queue.c @@ -1,17 +1,14 @@ #include "lfqueue.h" void queue_init(Queue *queue, void (*destroy)(void *data)) { - ll_init(queue, destroy); + ll_init(queue, destroy, NULL); } void queue_destroy(Queue *queue) { ll_destroy(queue); } -int queue_enqueue(Queue *queue, void *data) { - if (queue->size == 0) { - return ll_ins_next(queue, NULL, data); - } +int queue_enqueue(Queue *queue, const void *data) { return ll_ins_next(queue, queue->tail, data); } blob - e9ea33f070449ed8609fba04178972b065dba6a4 blob + 9370b765ba442a103da7c95ae4cafb9809a705e6 --- src/set.c +++ src/set.c @@ -2,8 +2,7 @@ void set_init(Set *set, int (*match)(const void *a, const void *b), void (*destroy)(void *data)) { - ll_init(set, destroy); - set->match = match; + ll_init(set, destroy, match); } void set_destroy(Set *set) { @@ -18,6 +17,9 @@ int set_insert(Set *set, const void *data) { } int set_remove(Set *set, void **data) { + if (data == NULL) { + return -1; + } ListNode *node = ll_find(set, *data); if (node == NULL) { return -1; @@ -89,7 +91,7 @@ int set_difference(Set *setd, const Set *a, const Set } int set_is_member(const Set *set, const void *data) { - return ll_find((List *)set, data) != NULL; + return ll_find(set, data) != NULL; } int set_is_subset(const Set *a, const Set *b) { blob - f8d1c4953df8c14efac4525ba9cf646abf697df6 blob + 7e0123a98021a825274196aed34c7e5aecb5480d --- src/stack.c +++ src/stack.c @@ -1,7 +1,7 @@ #include "lfstack.h" void stack_init(Stack *stack, void (*destroy)(void *data)) { - ll_init(stack, destroy); + ll_init(stack, destroy, NULL); } void stack_destroy(Stack *stack) { blob - a39bd8b1795b4f994bca76be4054f8820f1fa1c0 blob + 77e8f45842e4a65ef489d3d6940f5a23bbfffcc1 --- src/string.c +++ src/string.c @@ -4,18 +4,19 @@ #include "lfstring.h" -int find_substrings(const char* haystack, const char* needle, size_t *num_substrings, size_t **substrings) { +int str_find(const char* haystack, const char* needle, size_t *num_substrings, size_t **substrings) { if (*substrings != NULL) { return 1; } + *num_substrings = 0; + size_t sz_h = strlen(haystack); size_t sz_n = strlen(needle); - if ((int)sz_h - (int)sz_n < 0) { + if (sz_n > sz_h) { return 0; } - *num_substrings = 0; for (size_t i = 0; i <= sz_h - sz_n; ++i) { if (strncmp(haystack + i, needle, sz_n) == 0) { ++(*num_substrings); @@ -41,21 +42,21 @@ int find_substrings(const char* haystack, const char* return 0; } -char* substr(const char* str, size_t idx, size_t len) { +char* str_substr(const char* str, size_t idx, size_t len) { size_t sz_str = strlen(str); if (sz_str < len || idx + len > sz_str) { return NULL; } - char *substr = malloc(sizeof(char) * (len + 1)); - if (substr == NULL) { + char *sub = malloc(sizeof(char) * (len + 1)); + if (sub == NULL) { return NULL; } - memcpy(substr, str + idx, len); - substr[len] = '\0'; + memcpy(sub, str + idx, len); + sub[len] = '\0'; - return substr; + return sub; } char *str_trim(const char *str) { @@ -184,3 +185,70 @@ char *str_to_lower(const char *str) { out[len] = '\0'; return out; } + +char **str_split(const char *str, const char *delim, int *count) { + *count = 0; + size_t delim_len = strlen(delim); + + if (delim_len == 0) { + char **result = malloc(sizeof(char *)); + if (result == NULL) { + return NULL; + } + result[0] = strdup(str); + if (result[0] == NULL) { + free(result); + return NULL; + } + *count = 1; + return result; + } + + /* count segments */ + int n = 1; + const char *p = str; + while ((p = strstr(p, delim)) != NULL) { + n++; + p += delim_len; + } + + char **result = malloc(sizeof(char *) * n); + if (result == NULL) { + return NULL; + } + + int i = 0; + p = str; + const char *next; + while ((next = strstr(p, delim)) != NULL) { + size_t seg_len = next - p; + result[i] = malloc(seg_len + 1); + if (result[i] == NULL) { + for (int j = 0; j < i; j++) { + free(result[j]); + } + free(result); + return NULL; + } + memcpy(result[i], p, seg_len); + result[i][seg_len] = '\0'; + i++; + p = next + delim_len; + } + + /* last segment */ + result[i] = strdup(p); + if (result[i] == NULL) { + for (int j = 0; j < i; j++) { + free(result[j]); + } + free(result); + return NULL; + } + *count = n; + return result; +} + +int str_contains(const char *str, const char *substr) { + return strstr(str, substr) != NULL; +} blob - 9330ab238d5ddcad1e3170a2bd13ee8171d2d716 blob + 5e347290ae4488a0d8be1d5f61fa2c54ea42eb3f --- src/utility.c +++ src/utility.c @@ -11,11 +11,19 @@ Point Point_new(int x, int y) { Point *Point_new_p(int x, int y) { Point *p = malloc(sizeof(struct Point)); + if (!p) { + return NULL; + } p->x = x; p->y = y; return p; } -int Point_cmp(const Point a, const Point b) { + +void Point_destroy(Point *p) { + free(p); +} + +int Point_eq(const Point a, const Point b) { if (a.x == b.x && a.y == b.y) { return 1; } @@ -32,4 +40,3 @@ int Point_cmp_p(const Point *a, const Point *b) { int Point_cmp_v(const void *a, const void *b) { return Point_cmp_p(a, b); } - blob - 4fcf4f00c195c3b8f19fab2c186ffad1ee7d1aa9 blob + 42f1e34652af4031642d8236205413ea425b8c1c --- src/vector.c +++ src/vector.c @@ -13,8 +13,6 @@ #define VEC_INIT_CAP 2 -static int (*vec_sort_cmp_fn)(const void *, const void *); - int vec_init(Vector *vec, void (*destroy)(void *data)) { return vec_init_with_capacity(vec, destroy, VEC_INIT_CAP); } @@ -36,19 +34,19 @@ int vec_init_with_capacity(Vector *vec, void (*destroy } static int vec_expand(Vector *vec, size_t new_cap) { - vec->capacity = new_cap; - vec->elements = reallocarray(vec->elements, new_cap, sizeof(void *)); - - if (vec->elements == NULL) { + void *tmp = reallocarray(vec->elements, new_cap, sizeof(void *)); + if (tmp == NULL) { return -1; } + vec->elements = tmp; + vec->capacity = new_cap; return 0; } static int vec_grow(Vector *const vec) { size_t new_cap; if (vec->capacity == 0) { - new_cap = VEC_INIT_CAP; ; + new_cap = VEC_INIT_CAP; } else { new_cap = vec->capacity * 2; } @@ -62,9 +60,13 @@ int vec_grow_to(Vector *vec, const size_t new_cap) { return vec_expand(vec, new_cap); } -void vec_clear(Vector *vec) { +int vec_clear(Vector *vec) { + void (*destroy)(void *data) = vec->destroy; vec_destroy(vec); - vec_init(vec, vec->destroy); + if (vec_init(vec, destroy) != 0) { + return -1; + } + return 0; } void vec_destroy(Vector *vec) { @@ -81,16 +83,12 @@ int vec_insert(Vector *vec, void *data, size_t index) return -1; } - if (vec_len(vec) + 1 >= vec->capacity) { + if (vec_len(vec) + 1 > vec->capacity) { if (vec_grow(vec) != 0) { return -1; } } - if (index > vec_len(vec)) { - return -1; - } - if (index < vec_len(vec)) { void *a = vec->elements[index]; vec->elements[index] = data; @@ -139,18 +137,28 @@ int vec_shrink(Vector *vec) { return 0; } - vec->capacity = vec_len(vec); + if (vec_len(vec) == 0) { + free(vec->elements); + vec->elements = NULL; + vec->capacity = 0; + return 0; + } + size_t new_cap = vec_len(vec); + #if !defined(__OpenBSD__) - vec->elements = reallocf(vec->elements, sizeof(void *) * vec->capacity); + void *tmp = reallocf(vec->elements, sizeof(void *) * new_cap); #else - vec->elements = reallocarray(vec->elements, vec->capacity, sizeof(void *)); + void *tmp = reallocarray(vec->elements, new_cap, sizeof(void *)); #endif - if (vec->elements == NULL) { + if (tmp == NULL) { return -1; } + vec->elements = tmp; + vec->capacity = new_cap; + return 0; } @@ -199,19 +207,31 @@ void vec_reverse(Vector *vec) { } } -static int vec_sort_cmp_wrapper(const void *a, const void *b) { - /* qsort passes pointers to elements; our elements are void*, so a is void** */ +#if defined(__APPLE__) +static int vec_sort_cmp_wrapper(void *thunk, const void *a, const void *b) { + int (*cmp)(const void *, const void *) = thunk; const void *ea = *(const void **)a; const void *eb = *(const void **)b; - return vec_sort_cmp_fn(ea, eb); + return cmp(ea, eb); } +#else +static int vec_sort_cmp_wrapper(const void *a, const void *b, void *thunk) { + int (*cmp)(const void *, const void *) = thunk; + const void *ea = *(const void **)a; + const void *eb = *(const void **)b; + return cmp(ea, eb); +} +#endif void vec_sort(Vector *vec, int (*cmp)(const void *a, const void *b)) { if (vec_len(vec) < 2) { return; } - vec_sort_cmp_fn = cmp; - qsort(vec->elements, vec_len(vec), sizeof(void *), vec_sort_cmp_wrapper); +#if defined(__APPLE__) + qsort_r(vec->elements, vec_len(vec), sizeof(void *), (void *)cmp, vec_sort_cmp_wrapper); +#else + qsort_r(vec->elements, vec_len(vec), sizeof(void *), vec_sort_cmp_wrapper, (void *)cmp); +#endif } void *vec_bsearch(Vector *vec, const void *key, int (*cmp)(const void *a, const void *b)) { blob - fe79293ff6446784eaeff8ae652c0ab90891af70 blob + 3346d0cbbfc1802e839c173c5fe00ab6e0176fec --- tests/test_binarytree.c +++ tests/test_binarytree.c @@ -1,9 +1,26 @@ #include #include +#include #include "lftest.h" #include "lfbinarytree.h" +static int destroy_count; + +static void counting_destroy(void *data) { + (void)data; + destroy_count++; +} + +/* Traversal helper: append int values to a static buffer */ +static int traversal_buf[32]; +static int traversal_idx; + +static void collect_int(void *data) { + traversal_buf[traversal_idx++] = *(int *)data; +} + int main() { + /* Basic insertion and data access */ BinTree *tree = malloc(sizeof(BinTree)); bintree_init(tree, NULL); @@ -13,22 +30,162 @@ int main() { int r1 = 12; int r2 = 200; - bintree_ins_left(tree, NULL, &root); + ASSERT_EQ(bintree_ins_left(tree, NULL, &root), 0); ASSERT_NOT_NULL(tree->root); + ASSERT_EQ(tree->size, 1); - bintree_ins_left(tree, tree->root, &l1); - bintree_ins_left(tree, tree->root->left, &l2); - bintree_ins_right(tree, tree->root->left, &r2); - bintree_ins_right(tree, tree->root, &r1); - bintree_ins_right(tree, tree->root->right, &r2); - bintree_ins_left(tree, tree->root->right, &l1); + ASSERT_EQ(bintree_ins_left(tree, tree->root, &l1), 0); + ASSERT_EQ(bintree_ins_left(tree, tree->root->left, &l2), 0); + ASSERT_EQ(bintree_ins_right(tree, tree->root->left, &r2), 0); + ASSERT_EQ(bintree_ins_right(tree, tree->root, &r1), 0); + ASSERT_EQ(bintree_ins_right(tree, tree->root->right, &r2), 0); + ASSERT_EQ(bintree_ins_left(tree, tree->root->right, &l1), 0); + ASSERT_EQ(tree->size, 7); ASSERT_EQ(*(int *)tree->root->data, 0); ASSERT_EQ(*(int *)tree->root->left->data, 1); ASSERT_EQ(*(int *)tree->root->right->data, 12); + /* Error: insert root when tree already has one */ + ASSERT_EQ(bintree_ins_left(tree, NULL, &root), -1); + ASSERT_EQ(bintree_ins_right(tree, NULL, &root), -1); + + /* Error: insert left/right where child already exists */ + ASSERT_EQ(bintree_ins_left(tree, tree->root, &l1), -1); + ASSERT_EQ(bintree_ins_right(tree, tree->root, &r1), -1); + + /* Macros */ + ASSERT_FALSE(bintree_is_eob(tree->root)); + ASSERT_TRUE(bintree_is_eob(NULL)); + ASSERT_FALSE(bintree_is_leaf(tree->root)); + ASSERT_TRUE(bintree_is_leaf(tree->root->left->left)); + bintree_destroy(tree); + ASSERT_EQ(tree->size, 0); + ASSERT_NULL(tree->root); free(tree); + /* Destroy callback */ + destroy_count = 0; + BinTree dt; + bintree_init(&dt, counting_destroy); + + int a = 10, b = 20, c = 30; + bintree_ins_left(&dt, NULL, &a); + bintree_ins_left(&dt, dt.root, &b); + bintree_ins_right(&dt, dt.root, &c); + ASSERT_EQ(dt.size, 3); + + bintree_destroy(&dt); + ASSERT_EQ(destroy_count, 3); + + /* Partial removal with destroy callback */ + destroy_count = 0; + BinTree dt2; + bintree_init(&dt2, counting_destroy); + bintree_ins_left(&dt2, NULL, &a); + bintree_ins_left(&dt2, dt2.root, &b); + bintree_ins_right(&dt2, dt2.root, &c); + bintree_rem_left(&dt2, dt2.root); + ASSERT_EQ(destroy_count, 1); + ASSERT_EQ(dt2.size, 2); + ASSERT_NULL(dt2.root->left); + bintree_destroy(&dt2); + ASSERT_EQ(destroy_count, 3); + + /* Traversal: build tree + * 1 + * / \ + * 2 3 + * / \ + * 4 5 + */ + BinTree tt; + bintree_init(&tt, NULL); + int v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5; + bintree_ins_left(&tt, NULL, &v1); + bintree_ins_left(&tt, tt.root, &v2); + bintree_ins_right(&tt, tt.root, &v3); + bintree_ins_left(&tt, tt.root->left, &v4); + bintree_ins_right(&tt, tt.root->left, &v5); + + /* Preorder: 1 2 4 5 3 */ + traversal_idx = 0; + bintree_traverse(&tt, tt.root, BINTREE_PREORDER, collect_int); + ASSERT_EQ(traversal_idx, 5); + ASSERT_EQ(traversal_buf[0], 1); + ASSERT_EQ(traversal_buf[1], 2); + ASSERT_EQ(traversal_buf[2], 4); + ASSERT_EQ(traversal_buf[3], 5); + ASSERT_EQ(traversal_buf[4], 3); + + /* Inorder: 4 2 5 1 3 */ + traversal_idx = 0; + bintree_traverse(&tt, tt.root, BINTREE_INORDER, collect_int); + ASSERT_EQ(traversal_idx, 5); + ASSERT_EQ(traversal_buf[0], 4); + ASSERT_EQ(traversal_buf[1], 2); + ASSERT_EQ(traversal_buf[2], 5); + ASSERT_EQ(traversal_buf[3], 1); + ASSERT_EQ(traversal_buf[4], 3); + + /* Postorder: 4 5 2 3 1 */ + traversal_idx = 0; + bintree_traverse(&tt, tt.root, BINTREE_POSTORDER, collect_int); + ASSERT_EQ(traversal_idx, 5); + ASSERT_EQ(traversal_buf[0], 4); + ASSERT_EQ(traversal_buf[1], 5); + ASSERT_EQ(traversal_buf[2], 2); + ASSERT_EQ(traversal_buf[3], 3); + ASSERT_EQ(traversal_buf[4], 1); + + /* Traversal on NULL node is a no-op */ + traversal_idx = 0; + bintree_traverse(&tt, NULL, BINTREE_INORDER, collect_int); + ASSERT_EQ(traversal_idx, 0); + + bintree_destroy(&tt); + + /* Merge */ + BinTree ml, mr, mm; + int mroot = 100, mlv = 10, mrv = 20; + bintree_init(&ml, NULL); + bintree_init(&mr, NULL); + bintree_ins_left(&ml, NULL, &mlv); + bintree_ins_left(&mr, NULL, &mrv); + + ASSERT_EQ(bintree_merge(&mm, &ml, &mr, &mroot), 0); + ASSERT_EQ(mm.size, 3); + ASSERT_EQ(*(int *)mm.root->data, 100); + ASSERT_EQ(*(int *)mm.root->left->data, 10); + ASSERT_EQ(*(int *)mm.root->right->data, 20); + ASSERT_EQ(ml.size, 0); + ASSERT_NULL(ml.root); + ASSERT_EQ(mr.size, 0); + ASSERT_NULL(mr.root); + bintree_destroy(&mm); + + /* Merge: left == right should fail */ + BinTree self; + bintree_init(&self, NULL); + bintree_ins_left(&self, NULL, &a); + BinTree merged; + ASSERT_EQ(bintree_merge(&merged, &self, &self, &b), -1); + bintree_destroy(&self); + + /* Size tracking through insertions and removals */ + BinTree st; + bintree_init(&st, NULL); + ASSERT_EQ(st.size, 0); + bintree_ins_left(&st, NULL, &a); + ASSERT_EQ(st.size, 1); + bintree_ins_left(&st, st.root, &b); + ASSERT_EQ(st.size, 2); + bintree_ins_right(&st, st.root, &c); + ASSERT_EQ(st.size, 3); + bintree_rem_right(&st, st.root); + ASSERT_EQ(st.size, 2); + bintree_destroy(&st); + TEST_REPORT(); } blob - 42b3af2038c8a4078c1f6260551b08f1dd30c637 blob + a7a6a4e8933defb45ed9cdec55444cd752cfc482 --- tests/test_crypto.c +++ tests/test_crypto.c @@ -5,42 +5,42 @@ #include "lfcrypto.h" int main() { - char *in = "BUTT"; - unsigned char *s = b64_encode(in, strlen(in)); - ASSERT_STR_EQ((char *)s, "QlVUVA=="); - free(s); + const unsigned char *in = (const unsigned char *)"BUTT"; + char *cs = b64_encode(in, strlen((const char *)in)); + ASSERT_STR_EQ(cs, "QlVUVA=="); + free(cs); - char *in2 = "a longer base64 test, apparently"; - s = b64_encode(in2, strlen(in2)); - ASSERT_STR_EQ((char *)s, "YSBsb25nZXIgYmFzZTY0IHRlc3QsIGFwcGFyZW50bHk="); - free(s); + const unsigned char *in2 = (const unsigned char *)"a longer base64 test, apparently"; + cs = b64_encode(in2, strlen((const char *)in2)); + ASSERT_STR_EQ(cs, "YSBsb25nZXIgYmFzZTY0IHRlc3QsIGFwcGFyZW50bHk="); + free(cs); char *out2 = "YSBsb25nZXIgYmFzZTY0IHRlc3QsIGFwcGFyZW50bHk="; size_t s_sz = 0; - s = (unsigned char *) b64_decode(out2, strlen(out2), &s_sz); - ASSERT_STR_EQ((char *)s, "a longer base64 test, apparently"); - ASSERT_EQ(strlen((char *)s), s_sz); - free(s); + unsigned char *us = b64_decode(out2, strlen(out2), &s_sz); + ASSERT_STR_EQ((char *)us, "a longer base64 test, apparently"); + ASSERT_EQ(strlen((char *)us), s_sz); + free(us); - s = hex_decode("DEADBEEF", &s_sz); + us = hex_decode("DEADBEEF", &s_sz); unsigned char h[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; for (size_t i = 0; i < 4; ++i) { - ASSERT_EQ(s[i], h[i]); + ASSERT_EQ(us[i], h[i]); } - free(s); + free(us); - s = hex_decode("f00f5", &s_sz); + us = hex_decode("f00f5", &s_sz); unsigned char h2[3] = { 0x0F, 0x00, 0xF5 }; for (size_t i = 0; i < 3; ++i) { - ASSERT_EQ(s[i], h2[i]); + ASSERT_EQ(us[i], h2[i]); } - free(s); + free(us); - s = hex_decode("0xf00f5", &s_sz); + us = hex_decode("0xf00f5", &s_sz); for (size_t i = 0; i < 3; ++i) { - ASSERT_EQ(s[i], h2[i]); + ASSERT_EQ(us[i], h2[i]); } - free(s); + free(us); char *enc_s = hex_encode(h, 4); ASSERT_STR_EQ(enc_s, "deadbeef"); @@ -59,11 +59,73 @@ int main() { unsigned char ua[2] = {0x2, 0xF}; unsigned char ub[2] = {0x4, 0xE}; - unsigned int hamming = hamming_distance(ua, ub, 2); + int hamming = hamming_distance(ua, ub, 2); ASSERT_EQ(hamming, 3); hamming = hamming_distance_s("this is a test", "wokka wokka!!!"); ASSERT_EQ(hamming, 37); + /* -- expanded tests -- */ + + /* empty b64 encode/decode */ + cs = b64_encode((const unsigned char *)"", 0); + ASSERT_STR_EQ(cs, ""); + free(cs); + + us = b64_decode("", 0, &s_sz); + ASSERT_EQ(s_sz, (size_t)0); + free(us); + + /* empty hex encode */ + enc_s = hex_encode((const unsigned char *)"", 0); + ASSERT_STR_EQ(enc_s, ""); + free(enc_s); + + /* hex_to_str empty */ + enc_s = hex_to_str((const unsigned char *)"", 0); + ASSERT_STR_EQ(enc_s, ""); + free(enc_s); + + /* binary roundtrip through hex encode/decode */ + unsigned char bin[5] = { 0x00, 0xFF, 0x7F, 0x80, 0x01 }; + enc_s = hex_encode(bin, 5); + us = hex_decode(enc_s, &s_sz); + ASSERT_EQ(s_sz, (size_t)5); + for (size_t i = 0; i < 5; ++i) { + ASSERT_EQ(us[i], bin[i]); + } + free(enc_s); + free(us); + + /* hex_decode with NULL sz returns NULL */ + us = hex_decode("DEADBEEF", NULL); + ASSERT_NULL(us); + + /* hex_decode size validation */ + us = hex_decode("AB", &s_sz); + ASSERT_EQ(s_sz, (size_t)1); + ASSERT_EQ(us[0], (unsigned char)0xAB); + free(us); + + us = hex_decode("ABCD", &s_sz); + ASSERT_EQ(s_sz, (size_t)2); + free(us); + + /* hamming_distance_s mismatched lengths returns -1 */ + hamming = hamming_distance_s("short", "longer string"); + ASSERT_EQ(hamming, -1); + + /* hamming_distance empty strings */ + hamming = hamming_distance_s("", ""); + ASSERT_EQ(hamming, 0); + + /* hamming_distance identical strings */ + hamming = hamming_distance_s("same", "same"); + ASSERT_EQ(hamming, 0); + + /* repeating_key_xor with zero-length key returns NULL */ + xor_s = repeating_key_xor((const unsigned char *)"test", 4, (const unsigned char *)"", 0); + ASSERT_NULL(xor_s); + TEST_REPORT(); } blob - ff52d5816b27025a8199993784ea05baa32931ad blob + c4ac31580e34498b9875dd6a0bc03e7a2ec977a5 --- tests/test_linkedlist.c +++ tests/test_linkedlist.c @@ -1,10 +1,11 @@ #include #include +#include #include "lftest.h" #include "lflinkedlist.h" static int match_int(const void *a, const void *b) { - return *(const int *)a == *(const int *)b; + return *(const int *)a - *(const int *)b; } static int cmp_int(const void *a, const void *b) { @@ -14,9 +15,9 @@ static int cmp_int(const void *a, const void *b) { } int main() { - /* basic insert/remove */ + /* Basic insertion and size */ List *list = malloc(sizeof(List)); - ll_init(list, NULL); + ll_init(list, NULL, NULL); int i = 1; int j = 2; @@ -36,31 +37,165 @@ int main() { ll_destroy(list); free(list); - /* ll_find */ + /* ll_ins_prev */ list = malloc(sizeof(List)); - ll_init(list, NULL); - list->match = match_int; + ll_init(list, NULL, NULL); int a = 10, b = 20, c = 30; - ll_ins_next(list, list->head, &a); - ll_ins_next(list, list->tail, &b); - ll_ins_next(list, list->tail, &c); + ll_ins_next(list, list->head, &b); /* [20] */ + ll_ins_prev(list, list->head, &a); /* [10, 20] */ + ll_ins_next(list, list->tail, &c); /* [10, 20, 30] */ - ListNode *found = ll_find(list, &b); + ASSERT_EQ(list->size, 3); + ASSERT_EQ(*(int *)list->head->data, 10); + ASSERT_EQ(*(int *)list->head->next->data, 20); + ASSERT_EQ(*(int *)list->tail->data, 30); + + /* ll_ins_prev into middle */ + int d = 15; + ll_ins_prev(list, list->head->next, &d); /* [10, 15, 20, 30] */ + ASSERT_EQ(list->size, 4); + ASSERT_EQ(*(int *)list->head->next->data, 15); + + ll_destroy(list); + free(list); + + /* ll_remove direct */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int x = 100, y = 200, z = 300; + ll_ins_next(list, list->head, &x); + ll_ins_next(list, list->tail, &y); + ll_ins_next(list, list->tail, &z); + + /* Remove middle node */ + ll_remove(list, list->head->next, &data); + ASSERT_EQ(*(int *)data, 200); + ASSERT_EQ(list->size, 2); + ASSERT_EQ(*(int *)list->head->data, 100); + ASSERT_EQ(*(int *)list->tail->data, 300); + + /* Remove head */ + ll_remove(list, list->head, &data); + ASSERT_EQ(*(int *)data, 100); + ASSERT_EQ(list->size, 1); + ASSERT_EQ(*(int *)list->head->data, 300); + + /* Remove tail (last element) */ + ll_remove(list, list->tail, &data); + ASSERT_EQ(*(int *)data, 300); + ASSERT_EQ(list->size, 0); + ASSERT_NULL(list->head); + ASSERT_NULL(list->tail); + + ll_destroy(list); + free(list); + + /* ll_remove_prev */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int p = 1, q = 2, r = 3; + ll_ins_next(list, list->head, &p); + ll_ins_next(list, list->tail, &q); + ll_ins_next(list, list->tail, &r); + + /* Remove prev of tail (should remove middle) */ + ll_remove_prev(list, list->tail, &data); + ASSERT_EQ(*(int *)data, 2); + ASSERT_EQ(list->size, 2); + + /* Remove prev of head should fail */ + ASSERT_EQ(ll_remove_prev(list, list->head, &data), -1); + + ll_destroy(list); + free(list); + + /* ll_remove NULL data pointer */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int val = 42; + ll_ins_next(list, list->head, &val); + ASSERT_EQ(ll_remove(list, list->head, NULL), -1); + ASSERT_EQ(list->size, 1); + + ll_destroy(list); + free(list); + + /* Destroy callback with free (heap-allocated data) */ + list = malloc(sizeof(List)); + ll_init(list, free, NULL); + + for (int n = 0; n < 5; n++) { + int *heap_val = malloc(sizeof(int)); + *heap_val = n * 10; + ll_ins_next(list, list->tail, heap_val); + } + ASSERT_EQ(list->size, 5); + ll_destroy(list); + ASSERT_EQ(list->size, 0); + free(list); + + /* Error paths: NULL node for insert when list non-empty */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int e = 1; + ll_ins_next(list, list->head, &e); + ASSERT_EQ(ll_ins_next(list, NULL, &e), -1); + ASSERT_EQ(ll_ins_prev(list, NULL, &e), -1); + + /* Error paths: remove from empty list */ + List *empty = malloc(sizeof(List)); + ll_init(empty, NULL, NULL); + ASSERT_EQ(ll_remove(empty, NULL, &data), -1); + ASSERT_EQ(ll_remove_next(empty, NULL, &data), -1); + ASSERT_EQ(ll_remove_prev(empty, NULL, &data), -1); + free(empty); + + ll_destroy(list); + free(list); + + /* ll_find with match callback */ + list = malloc(sizeof(List)); + ll_init(list, NULL, match_int); + + int f1 = 10, f2 = 20, f3 = 30; + ll_ins_next(list, list->head, &f1); + ll_ins_next(list, list->tail, &f2); + ll_ins_next(list, list->tail, &f3); + + int search = 20; + ListNode *found = ll_find(list, &search); ASSERT_NOT_NULL(found); ASSERT_EQ(*(int *)found->data, 20); int missing = 99; - found = ll_find(list, &missing); - ASSERT_NULL(found); + ASSERT_NULL(ll_find(list, &missing)); - /* ll_find with no match callback */ + /* ll_find with NULL match (pointer comparison) */ list->match = NULL; - found = ll_find(list, &a); - ASSERT_NULL(found); - list->match = match_int; + found = ll_find(list, &f2); + ASSERT_NOT_NULL(found); + ASSERT_EQ(*(int *)found->data, 20); + ASSERT_NULL(ll_find(list, &missing)); + ASSERT_NULL(ll_find(NULL, &search)); + + ll_destroy(list); + free(list); + /* ll_reverse */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int ra = 10, rb = 20, rc = 30; + ll_ins_next(list, list->head, &ra); + ll_ins_next(list, list->tail, &rb); + ll_ins_next(list, list->tail, &rc); + ll_reverse(list); ASSERT_EQ(*(int *)list->head->data, 30); ASSERT_EQ(*(int *)list->tail->data, 10); @@ -78,20 +213,12 @@ int main() { ASSERT_EQ(*(int *)node->data, expected_fwd[idx++]); } - /* ll_to_array */ - void **arr = ll_to_array(list); - ASSERT_NOT_NULL(arr); - ASSERT_EQ(*(int *)arr[0], 30); - ASSERT_EQ(*(int *)arr[1], 20); - ASSERT_EQ(*(int *)arr[2], 10); - free(arr); - ll_destroy(list); free(list); /* ll_sort */ list = malloc(sizeof(List)); - ll_init(list, NULL); + ll_init(list, NULL, NULL); int s1 = 3, s2 = 1, s3 = 5, s4 = 2, s5 = 4; ll_ins_next(list, list->head, &s1); @@ -119,7 +246,7 @@ int main() { /* sort empty and single-element lists */ ll_destroy(list); - ll_init(list, NULL); + ll_init(list, NULL, NULL); ll_sort(list, cmp_int); ASSERT_EQ(list->size, 0); @@ -129,11 +256,47 @@ int main() { ASSERT_EQ(list->size, 1); ASSERT_EQ(*(int *)list->head->data, 42); - /* ll_to_array on empty list */ ll_destroy(list); - ll_init(list, NULL); + free(list); + + /* ll_to_array: single element */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int single = 42; + ll_ins_next(list, list->head, &single); + + void **arr = ll_to_array(list); + ASSERT_NOT_NULL(arr); + ASSERT_EQ(*(int *)arr[0], 42); + free(arr); + + ll_destroy(list); + free(list); + + /* ll_to_array: empty list */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + ASSERT_NULL(ll_to_array(list)); + ASSERT_NULL(ll_to_array(NULL)); + ll_destroy(list); + free(list); + + /* ll_to_array: multiple elements */ + list = malloc(sizeof(List)); + ll_init(list, NULL, NULL); + + int m1 = 1, m2 = 2, m3 = 3; + ll_ins_next(list, list->head, &m1); + ll_ins_next(list, list->tail, &m2); + ll_ins_next(list, list->tail, &m3); + arr = ll_to_array(list); - ASSERT_NULL(arr); + ASSERT_NOT_NULL(arr); + ASSERT_EQ(*(int *)arr[0], 1); + ASSERT_EQ(*(int *)arr[1], 2); + ASSERT_EQ(*(int *)arr[2], 3); + free(arr); ll_destroy(list); free(list); blob - /dev/null blob + ba4d898681161690dee7dbb49800e4f0771585a7 (mode 644) --- /dev/null +++ tests/test_input.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include "lftest.h" +#include "lfinput.h" + +static void test_split_basic(void) { + char *s = strdup("one,two,three"); + size_t n = 0; + char **parts = inp_split(s, &n, ","); + ASSERT_NOT_NULL(parts); + ASSERT_EQ(n, 3); + ASSERT_STR_EQ(parts[0], "one"); + ASSERT_STR_EQ(parts[1], "two"); + ASSERT_STR_EQ(parts[2], "three"); + inp_del_split(parts); + free(s); +} + +static void test_split_single(void) { + char *s = strdup("hello"); + size_t n = 0; + char **parts = inp_split(s, &n, ","); + ASSERT_NOT_NULL(parts); + ASSERT_EQ(n, 1); + ASSERT_STR_EQ(parts[0], "hello"); + inp_del_split(parts); + free(s); +} + +static void test_split_null(void) { + size_t n = 0; + ASSERT_NULL(inp_split(NULL, &n, ",")); + char *s = strdup("a,b"); + ASSERT_NULL(inp_split(s, NULL, ",")); + free(s); + s = strdup("a,b"); + ASSERT_NULL(inp_split(s, &n, NULL)); + free(s); +} + +static void test_del_split_null(void) { + inp_del_split(NULL); /* should not crash */ +} + +static void test_del_lines_null(void) { + inp_del_lines(NULL); /* should not crash */ +} + +static void test_capture_system(void) { + char *out = inp_capture_system("echo hello", 0); + ASSERT_NOT_NULL(out); + ASSERT_STR_EQ(out, "hello\n"); + free(out); +} + +static void test_capture_system_null(void) { + ASSERT_NULL(inp_capture_system(NULL, 0)); +} + +static void test_capture_system_large(void) { + /* Generate output larger than the default buffer size */ + char *out = inp_capture_system("seq 1 1000", 32); + ASSERT_NOT_NULL(out); + /* Check it contains the last line */ + ASSERT_TRUE(strstr(out, "1000") != NULL); + free(out); +} + +static void test_file_roundtrip(void) { + /* Write a temp file, read it back */ + const char *tmppath = "/tmp/libflint_test_input.txt"; + FILE *f = fopen(tmppath, "w"); + ASSERT_NOT_NULL(f); + fprintf(f, "line1\nline2\nline3\n"); + fclose(f); + + char *contents = inp_get_input(tmppath); + ASSERT_NOT_NULL(contents); + ASSERT_STR_EQ(contents, "line1\nline2\nline3\n"); + free(contents); + + size_t n = 0; + char **lines = inp_get_lines(tmppath, &n); + ASSERT_NOT_NULL(lines); + ASSERT_EQ(n, 3); + ASSERT_STR_EQ(lines[0], "line1"); + ASSERT_STR_EQ(lines[1], "line2"); + ASSERT_STR_EQ(lines[2], "line3"); + inp_del_lines(lines); + + remove(tmppath); +} + +static void test_get_binary(void) { + const char *tmppath = "/tmp/libflint_test_input.bin"; + FILE *f = fopen(tmppath, "wb"); + ASSERT_NOT_NULL(f); + unsigned char data[] = {0x00, 0x01, 0x02, 0xFF}; + fwrite(data, 1, sizeof(data), f); + fclose(f); + + size_t fsz = 0; + unsigned char *buf = inp_get_binary(tmppath, &fsz); + ASSERT_NOT_NULL(buf); + ASSERT_EQ(fsz, 4); + ASSERT_EQ(buf[0], 0x00); + ASSERT_EQ(buf[3], 0xFF); + free(buf); + + remove(tmppath); +} + +static void test_get_ints(void) { + const char *tmppath = "/tmp/libflint_test_ints.txt"; + FILE *f = fopen(tmppath, "w"); + ASSERT_NOT_NULL(f); + fprintf(f, "10\n20\n30\n"); + fclose(f); + + size_t n = 0; + int *ints = inp_get_ints(tmppath, &n); + ASSERT_NOT_NULL(ints); + ASSERT_EQ(n, 3); + ASSERT_EQ(ints[0], 10); + ASSERT_EQ(ints[1], 20); + ASSERT_EQ(ints[2], 30); + free(ints); + + remove(tmppath); +} + +static void test_get_ints_null(void) { + size_t n = 0; + ASSERT_NULL(inp_get_ints(NULL, &n)); + ASSERT_NULL(inp_get_ints("/tmp/nonexistent_libflint", NULL)); +} + +int main(void) { + test_split_basic(); + test_split_single(); + test_split_null(); + test_del_split_null(); + test_del_lines_null(); + test_capture_system(); + test_capture_system_null(); + test_capture_system_large(); + test_file_roundtrip(); + test_get_binary(); + test_get_ints(); + test_get_ints_null(); + + TEST_REPORT(); +} blob - 48c994ca9950ca8f7a752bc05da4cd93b1d76514 blob + 262b329f842039d67b578681399b563fef392c93 --- tests/test_macos.c +++ tests/test_macos.c @@ -8,16 +8,39 @@ int main() { pid_t pid = getpid(); + + /* new_ProcessData returns zeroed memory with sentinel */ ProcessData *pd = new_ProcessData(); ASSERT_NOT_NULL(pd); + ASSERT_EQ(pd->percent_cpu, 0.0); + ASSERT_EQ(pd->virtual_memory, 0); + ASSERT_EQ(pd->resident_memory, 0); + ASSERT_EQ(pd->total_user_time, 0.0); + ASSERT_EQ(pd->total_kernel_time, 0.0); - for (int i = 0; i < 2; i++) { - update_process(pid, pd); - sleep(1); - } + /* first update succeeds and populates fields */ + int rc = update_process(pid, pd); + ASSERT_EQ(rc, 0); + ASSERT_TRUE(pd->resident_memory > 0); + ASSERT_TRUE(pd->virtual_memory > 0); + ASSERT_TRUE(pd->percent_cpu == 0.0); + + sleep(1); + + /* second update succeeds */ + rc = update_process(pid, pd); + ASSERT_EQ(rc, 0); ASSERT_TRUE(pd->percent_cpu >= 0.0); - free(pd); + /* error path: invalid PID returns -1 */ + rc = update_process(-1, pd); + ASSERT_EQ(rc, -1); + + destroy_ProcessData(pd); + + /* destroy_ProcessData handles NULL safely */ + destroy_ProcessData(NULL); + TEST_REPORT(); } #else blob - 50629b4a980d0cab64d560728ae38875b324dfb6 blob + b55e738cf14edadb083270493ac21260e5cddfd6 --- tests/test_math.c +++ tests/test_math.c @@ -1,28 +1,41 @@ +#include #include #include #include "lftest.h" #include "lfmath.h" int main() { - int i = 1, j = 2; - ASSERT_EQ(max_int(i, j), j); - ASSERT_EQ(min_int(i, j), i); - - char *s = "10101101"; - ASSERT_EQ(binstr_to_int(s), 173); - - char *s2 = "1010_1101"; - ASSERT_EQ(binstr_to_int(s2), 173); - - i = 10; - i = clamp_int(i, 2, 5); - ASSERT_EQ(i, 5); - /* abs_int */ ASSERT_EQ(abs_int(5), 5); ASSERT_EQ(abs_int(-5), 5); ASSERT_EQ(abs_int(0), 0); + ASSERT_EQ(abs_int(INT_MIN), INT_MAX); + /* max/min */ + ASSERT_EQ(max_int(1, 2), 2); + ASSERT_EQ(min_int(1, 2), 1); + + /* binstr_to_int */ + ASSERT_EQ(binstr_to_int("10101101"), 173); + ASSERT_EQ(binstr_to_int("1010_1101"), 173); + ASSERT_EQ(binstr_to_int(NULL), -1); + + /* clamp_int - all branches */ + ASSERT_EQ(clamp_int(10, 2, 5), 5); /* above high */ + ASSERT_EQ(clamp_int(1, 2, 5), 2); /* below low */ + ASSERT_EQ(clamp_int(3, 2, 5), 3); /* in range */ + ASSERT_EQ(clamp_int(3, 5, 2), 3); /* low > high, swap */ + ASSERT_EQ(clamp_int(10, 5, 2), 5); /* low > high, above */ + + /* is_power_of_two */ + ASSERT_TRUE(is_power_of_two(1)); + ASSERT_TRUE(is_power_of_two(2)); + ASSERT_TRUE(is_power_of_two(4)); + ASSERT_TRUE(is_power_of_two(1024)); + ASSERT_FALSE(is_power_of_two(0)); + ASSERT_FALSE(is_power_of_two(3)); + ASSERT_FALSE(is_power_of_two(-1)); + /* gcd */ ASSERT_EQ(gcd(12, 8), 4); ASSERT_EQ(gcd(7, 13), 1); @@ -51,11 +64,36 @@ int main() { ASSERT_EQ(isqrt(100), 10); ASSERT_EQ(isqrt(-1), -1); + /* bresenham coordinate verification */ size_t sz = 0; Point *line = bresenham(0, 0, 2, 5, &sz); + ASSERT_NOT_NULL(line); ASSERT_TRUE(sz > 0); + /* first point */ + ASSERT_EQ(line[0].x, 0); + ASSERT_EQ(line[0].y, 0); + /* last point */ + ASSERT_EQ(line[sz - 1].x, 2); + ASSERT_EQ(line[sz - 1].y, 5); + free(line); + + /* bresenham single point */ + line = bresenham(3, 3, 3, 3, &sz); ASSERT_NOT_NULL(line); + ASSERT_EQ(sz, 1); + ASSERT_EQ(line[0].x, 3); + ASSERT_EQ(line[0].y, 3); free(line); + /* bresenham_p */ + Point p1 = Point_new(0, 0); + Point p2 = Point_new(4, 0); + line = bresenham_p(p1, p2, &sz); + ASSERT_NOT_NULL(line); + ASSERT_EQ(sz, 5); + ASSERT_EQ(line[0].x, 0); + ASSERT_EQ(line[4].x, 4); + free(line); + TEST_REPORT(); } blob - 6fc0580bc98896e1b56234a8c7a163f12e7c9289 blob + 43153093ad2f9e586c56f012db8fb3943526d80a --- tests/test_memory.c +++ tests/test_memory.c @@ -5,7 +5,7 @@ int main() { ArenaAllocator *arena = malloc(sizeof(ArenaAllocator)); - arena_init(arena, 1024); + ASSERT_EQ(arena_init(arena, 1024), 0); int *i1 = arena_malloc(arena, sizeof(int)); int *i2 = arena_malloc(arena, sizeof(int)); @@ -43,24 +43,31 @@ int main() { ASSERT_NOT_NULL(after_clear); /* Test arena_resize_buf */ - arena_resize_buf(arena, 2048); + ASSERT_EQ(arena_resize_buf(arena, 2048), 0); void *big_alloc = arena_malloc(arena, 1500); ASSERT_NOT_NULL(big_alloc); + /* Test arena_resize_buf rejects shrink below offset_cur */ + ASSERT_EQ(arena_resize_buf(arena, 1), -1); + + /* Test arena_init returns -1 for NULL */ + ASSERT_EQ(arena_init(NULL, 64), -1); + arena_free(arena); free(arena); /* Test stack-allocated arena */ ArenaAllocator stack_arena; - arena_init(&stack_arena, 256); + ASSERT_EQ(arena_init(&stack_arena, 256), 0); int *si = arena_malloc(&stack_arena, sizeof(int)); ASSERT_NOT_NULL(si); *si = 99; ASSERT_EQ(*si, 99); arena_free(&stack_arena); + /* Pool tests */ PoolAllocator *pool = malloc(sizeof(PoolAllocator)); - pool_init(pool, 64, 16, LF_DEFAULT_ALIGNMENT); + ASSERT_EQ(pool_init(pool, 64, 16, LF_DEFAULT_ALIGNMENT), 0); void *a = pool_alloc(pool); void *b = pool_alloc(pool); void *c = pool_alloc(pool); @@ -77,7 +84,7 @@ int main() { /* Test pool_free_all resets properly */ pool_free_all(pool); - size_t chunk_count = 64 / 16; + size_t chunk_count = pool->buf_sz / pool->chunk_size; ASSERT_EQ(pool_count_available(pool), chunk_count); /* Allocate again after free_all to verify no corruption */ @@ -87,7 +94,50 @@ int main() { ASSERT_NOT_NULL(b); pool_destroy(pool); + free(pool); + /* Pool exhaustion: allocating more chunks than capacity returns NULL */ + PoolAllocator pool_ex; + ASSERT_EQ(pool_init(&pool_ex, 64, 16, LF_DEFAULT_ALIGNMENT), 0); + size_t ex_count = pool_ex.buf_sz / pool_ex.chunk_size; + for (size_t i = 0; i < ex_count; ++i) { + ASSERT_NOT_NULL(pool_alloc(&pool_ex)); + } + ASSERT_NULL(pool_alloc(&pool_ex)); + pool_destroy(&pool_ex); + + /* Pool free with bad pointers (outside buffer range) */ + PoolAllocator pool_bad; + ASSERT_EQ(pool_init(&pool_bad, 64, 16, LF_DEFAULT_ALIGNMENT), 0); + size_t before = pool_count_available(&pool_bad); + int dummy = 0; + pool_free(&pool_bad, &dummy); /* pointer outside pool */ + pool_free(&pool_bad, NULL); /* NULL pointer */ + ASSERT_EQ(pool_count_available(&pool_bad), before); /* count unchanged */ + pool_destroy(&pool_bad); + + /* Pool free + realloc cycle */ + PoolAllocator pool_cycle; + ASSERT_EQ(pool_init(&pool_cycle, 64, 16, LF_DEFAULT_ALIGNMENT), 0); + /* drain all chunks first */ + size_t cycle_count = pool_cycle.buf_sz / pool_cycle.chunk_size; + for (size_t i = 0; i < cycle_count; ++i) { + pool_alloc(&pool_cycle); + } + ASSERT_EQ(pool_count_available(&pool_cycle), 0); + /* free one, then alloc -- must get it back */ + void *p1 = &pool_cycle.buf[pool_cycle.aligned_start]; /* first chunk addr */ + pool_free(&pool_cycle, p1); + void *p2 = pool_alloc(&pool_cycle); + ASSERT_NOT_NULL(p2); + ASSERT_EQ(p1, p2); /* should reuse the freed chunk */ + pool_destroy(&pool_cycle); + + /* Pool init with bad params */ + PoolAllocator pool_bp; + ASSERT_EQ(pool_init(&pool_bp, 4, 16, LF_DEFAULT_ALIGNMENT), -1); /* buf too small */ + ASSERT_EQ(pool_init(NULL, 64, 16, LF_DEFAULT_ALIGNMENT), -1); /* NULL allocator */ + /* arena save/restore */ ArenaAllocator save_arena; arena_init(&save_arena, 512); blob - 9fa89c42d0b00b733699a8d0ad057431232d3eda blob + b1c95cf8ed22b0e2919ac7558c9b4e65077c41cf --- tests/test_network.c +++ tests/test_network.c @@ -46,17 +46,46 @@ static void *udp_server_thread(void *vargp) { return NULL; } +static void dummy_handler(Server *s) { + (void)s; +} + int main() { + /* Test new_server with bad port returns NULL */ + Server *bad = new_server(SERVERTYPE_TCP, "notaport", dummy_handler); + ASSERT_NULL(bad); + + /* Test new_server creates valid TCP server and delete_server cleans up */ + Server *tcp = new_server(SERVERTYPE_TCP, "18640", dummy_handler); + ASSERT_NOT_NULL(tcp); + ASSERT_EQ(tcp->server_type, SERVERTYPE_TCP); + ASSERT_EQ(tcp->port, 18640); + ASSERT_TRUE(tcp->fd >= 0); + delete_server(tcp); + + /* Test new_server creates valid UDP server and delete_server cleans up */ + Server *udp = new_server(SERVERTYPE_UDP, "18641", dummy_handler); + ASSERT_NOT_NULL(udp); + ASSERT_EQ(udp->server_type, SERVERTYPE_UDP); + ASSERT_EQ(udp->port, 18641); + ASSERT_TRUE(udp->fd >= 0); + delete_server(udp); + + /* Test delete_server with NULL doesn't crash */ + delete_server(NULL); + + /* TCP echo test */ pthread_t srv_tid; pthread_create(&srv_tid, NULL, tcp_server_thread, NULL); sleep(1); - const char *s = capture_system("echo hello | nc localhost 18632", 0); + char *s = inp_capture_system("echo hello | nc localhost 18632", 0); ASSERT_STR_EQ(s, NET_MSG); - free((char *) s); + free(s); pthread_join(srv_tid, NULL); + /* UDP test */ pthread_create(&srv_tid, NULL, udp_server_thread, NULL); sleep(1); system("echo hello | nc localhost 18633"); blob - 57abe442572fbbbc6526d2271a7729463d8b5d61 blob + 6247350fe666d76a5521f0f7b9a2cbae9382810c --- tests/test_parsing.c +++ tests/test_parsing.c @@ -7,5 +7,20 @@ int main() { char *english = "This is an English sentence!"; ASSERT_TRUE(simple_english_scoring(english) > simple_english_scoring(nonsense)); + /* NULL input */ + ASSERT_EQ(simple_english_scoring(NULL), -1); + + /* empty string */ + ASSERT_EQ(simple_english_scoring(""), 0); + + /* known absolute score: "the" = t(12) + h(5) + e(13) = 30 */ + ASSERT_EQ(simple_english_scoring("the"), 30); + + /* single space scores 7 */ + ASSERT_EQ(simple_english_scoring(" "), 7); + + /* unknown chars score 0 */ + ASSERT_EQ(simple_english_scoring("!!!"), 0); + TEST_REPORT(); } blob - 89facce7f60029c4e811cf64a51698d0f9ac07fc blob + 9006ae42082a6ff913cb0eee7cc7faaa21748fe8 --- tests/test_queue.c +++ tests/test_queue.c @@ -3,7 +3,15 @@ #include "lftest.h" #include "lfqueue.h" +static int destroyed_count; + +static void counting_destroy(void *data) { + (void)data; + destroyed_count++; +} + int main() { + /* basic enqueue/dequeue FIFO */ Queue *q = malloc(sizeof(Queue)); queue_init(q, NULL); @@ -11,32 +19,100 @@ int main() { queue_enqueue(q, &a); queue_enqueue(q, &b); queue_enqueue(q, &c); - ASSERT_EQ(q->size, 3); + ASSERT_EQ(queue_size(q), 3); + ASSERT_FALSE(queue_is_empty(q)); - /* peek returns front without removing */ int *p = queue_peek(q); ASSERT_EQ(*p, 1); - ASSERT_EQ(q->size, 3); + ASSERT_EQ(queue_size(q), 3); - /* FIFO order */ queue_dequeue(q, (void **)&p); ASSERT_EQ(*p, 1); queue_dequeue(q, (void **)&p); ASSERT_EQ(*p, 2); queue_dequeue(q, (void **)&p); ASSERT_EQ(*p, 3); - ASSERT_EQ(q->size, 0); + ASSERT_EQ(queue_size(q), 0); + ASSERT_TRUE(queue_is_empty(q)); /* peek on empty queue */ p = queue_peek(q); ASSERT_NULL(p); - /* dequeue on empty queue */ + /* dequeue on empty queue returns -1 */ int ret = queue_dequeue(q, (void **)&p); ASSERT_EQ(ret, -1); queue_destroy(q); free(q); + /* return values from enqueue/dequeue */ + q = malloc(sizeof(Queue)); + queue_init(q, NULL); + + ret = queue_enqueue(q, &a); + ASSERT_EQ(ret, 0); + ret = queue_dequeue(q, (void **)&p); + ASSERT_EQ(ret, 0); + ASSERT_EQ(*p, 1); + + queue_destroy(q); + free(q); + + /* destroy callback */ + q = malloc(sizeof(Queue)); + destroyed_count = 0; + queue_init(q, counting_destroy); + + int *d1 = malloc(sizeof(int)); + int *d2 = malloc(sizeof(int)); + *d1 = 10; *d2 = 20; + queue_enqueue(q, d1); + queue_enqueue(q, d2); + + queue_destroy(q); + ASSERT_EQ(destroyed_count, 2); + free(d1); free(d2); + free(q); + + /* enqueue after drain */ + q = malloc(sizeof(Queue)); + queue_init(q, NULL); + + queue_enqueue(q, &a); + queue_dequeue(q, (void **)&p); + ASSERT_TRUE(queue_is_empty(q)); + + queue_enqueue(q, &b); + ASSERT_EQ(queue_size(q), 1); + p = queue_peek(q); + ASSERT_EQ(*p, 2); + + queue_dequeue(q, (void **)&p); + ASSERT_EQ(*p, 2); + ASSERT_TRUE(queue_is_empty(q)); + + queue_destroy(q); + free(q); + + /* single-element operations */ + q = malloc(sizeof(Queue)); + queue_init(q, NULL); + + int v = 77; + queue_enqueue(q, &v); + ASSERT_EQ(queue_size(q), 1); + + p = queue_peek(q); + ASSERT_EQ(*p, 77); + + ret = queue_dequeue(q, (void **)&p); + ASSERT_EQ(ret, 0); + ASSERT_EQ(*p, 77); + ASSERT_TRUE(queue_is_empty(q)); + + queue_destroy(q); + free(q); + TEST_REPORT(); } blob - 278cc30436e944c2424022d7e15b7ed2b6d3e657 blob + 894da44be9bdb69a88e1342314d9b31fa360dc48 --- tests/test_set.c +++ tests/test_set.c @@ -1,13 +1,19 @@ #include #include +#include #include "lftest.h" #include "lfset.h" static int int_match(const void *a, const void *b) { - return *((int *) a) == *((int *) b); + return *(const int *)a - *(const int *)b; } +static void free_data(void *data) { + free(data); +} + int main() { + /* Basic insert and duplicate rejection */ Set *set = malloc(sizeof(Set)); set_init(set, int_match, NULL); @@ -15,12 +21,34 @@ int main() { int j = 2; int k = 2; - set_insert(set, &i); + ASSERT_EQ(set_insert(set, &i), 0); + ASSERT_EQ(set_insert(set, &j), 0); + ASSERT_EQ(set_insert(set, &k), 1); /* duplicate returns 1 */ + + ASSERT_EQ(set_size(set), 2); + + /* set_is_member */ + ASSERT_TRUE(set_is_member(set, &i)); + ASSERT_TRUE(set_is_member(set, &j)); + int missing = 99; + ASSERT_FALSE(set_is_member(set, &missing)); + + /* set_remove */ + void *removed = &j; + ASSERT_EQ(set_remove(set, &removed), 0); + ASSERT_EQ(set_size(set), 1); + ASSERT_FALSE(set_is_member(set, &j)); + + /* set_remove: not found */ + removed = &missing; + ASSERT_EQ(set_remove(set, &removed), -1); + + /* set_remove: NULL data pointer */ + ASSERT_EQ(set_remove(set, NULL), -1); + + /* Re-insert removed element for set operations */ set_insert(set, &j); - set_insert(set, &k); - ASSERT_EQ(set->size, 2); /* duplicate should not be inserted */ - int i2 = 1; int j2 = 4; @@ -37,20 +65,60 @@ int main() { set_difference(set_d, set, set2); set_intersection(set_i, set, set2); - ASSERT_EQ(set_u->size, 3); /* {1, 2, 4} */ - ASSERT_EQ(set_d->size, 1); /* {2} */ - ASSERT_EQ(set_i->size, 1); /* {1} */ + ASSERT_EQ(set_size(set_u), 3); /* {1, 2, 4} */ + ASSERT_EQ(set_size(set_d), 1); /* {2} */ + ASSERT_EQ(set_size(set_i), 1); /* {1} */ + /* set_is_subset */ + Set *sub = malloc(sizeof(Set)); + set_init(sub, int_match, NULL); + set_insert(sub, &i); /* {1} is subset of {1, 2} */ + ASSERT_TRUE(set_is_subset(sub, set)); + ASSERT_FALSE(set_is_subset(set, sub)); + + /* set_is_equal */ + Set *eq = malloc(sizeof(Set)); + set_init(eq, int_match, NULL); + set_insert(eq, &i); + set_insert(eq, &j); + ASSERT_TRUE(set_is_equal(set, eq)); + ASSERT_FALSE(set_is_equal(set, sub)); + set_destroy(set); set_destroy(set2); set_destroy(set_u); set_destroy(set_i); set_destroy(set_d); + set_destroy(sub); + set_destroy(eq); free(set); free(set2); free(set_u); free(set_i); free(set_d); + free(sub); + free(eq); + /* Heap-allocated data with destroy callback */ + Set *hset = malloc(sizeof(Set)); + set_init(hset, int_match, free_data); + + int *a = malloc(sizeof(int)); *a = 10; + int *b = malloc(sizeof(int)); *b = 20; + int *c = malloc(sizeof(int)); *c = 10; /* duplicate value */ + + ASSERT_EQ(set_insert(hset, a), 0); + ASSERT_EQ(set_insert(hset, b), 0); + ASSERT_EQ(set_insert(hset, c), 1); /* duplicate */ + free(c); /* caller responsible for rejected data */ + + ASSERT_EQ(set_size(hset), 2); + ASSERT_TRUE(set_is_member(hset, a)); + ASSERT_TRUE(set_is_member(hset, b)); + + /* destroy frees heap data */ + set_destroy(hset); + free(hset); + TEST_REPORT(); } blob - ae60a2174307462510938d3759e5dd662d312bdf blob + 0ef2e1eef7f25d0c07f3de0074bcfb23e14e4da0 --- tests/test_stack.c +++ tests/test_stack.c @@ -3,25 +3,110 @@ #include "lftest.h" #include "lfstack.h" +static int destroyed_count; + +static void counting_destroy(void *data) { + (void)data; + destroyed_count++; +} + int main() { + /* basic push/pop */ Stack *stack = malloc(sizeof(Stack)); stack_init(stack, NULL); int a = 1, b = 2; stack_push(stack, &a); stack_push(stack, &b); - ASSERT_EQ(stack->size, 2); + ASSERT_EQ(stack_size(stack), 2); + ASSERT_FALSE(stack_is_empty(stack)); int *p = NULL; - stack_pop(stack, (void **) &p); + stack_pop(stack, (void **)&p); ASSERT_EQ(*p, 2); - stack_pop(stack, (void **) &p); + stack_pop(stack, (void **)&p); ASSERT_EQ(*p, 1); - ASSERT_EQ(stack->size, 0); + ASSERT_EQ(stack_size(stack), 0); + ASSERT_TRUE(stack_is_empty(stack)); stack_destroy(stack); free(stack); + /* peek */ + stack = malloc(sizeof(Stack)); + stack_init(stack, NULL); + + ASSERT_NULL(stack_peek(stack)); + + int x = 42; + stack_push(stack, &x); + p = stack_peek(stack); + ASSERT_NOT_NULL(p); + ASSERT_EQ(*p, 42); + ASSERT_EQ(stack_size(stack), 1); + + stack_destroy(stack); + free(stack); + + /* empty pop returns -1 */ + stack = malloc(sizeof(Stack)); + stack_init(stack, NULL); + + int ret = stack_pop(stack, (void **)&p); + ASSERT_EQ(ret, -1); + + stack_destroy(stack); + free(stack); + + /* return values from push/pop */ + stack = malloc(sizeof(Stack)); + stack_init(stack, NULL); + + ret = stack_push(stack, &a); + ASSERT_EQ(ret, 0); + ret = stack_pop(stack, (void **)&p); + ASSERT_EQ(ret, 0); + + stack_destroy(stack); + free(stack); + + /* destroy callback */ + stack = malloc(sizeof(Stack)); + destroyed_count = 0; + stack_init(stack, counting_destroy); + + int *d1 = malloc(sizeof(int)); + int *d2 = malloc(sizeof(int)); + int *d3 = malloc(sizeof(int)); + *d1 = 10; *d2 = 20; *d3 = 30; + stack_push(stack, d1); + stack_push(stack, d2); + stack_push(stack, d3); + + stack_destroy(stack); + ASSERT_EQ(destroyed_count, 3); + free(d1); free(d2); free(d3); + free(stack); + + /* single-element push and pop */ + stack = malloc(sizeof(Stack)); + stack_init(stack, NULL); + + int v = 99; + stack_push(stack, &v); + ASSERT_EQ(stack_size(stack), 1); + + p = stack_peek(stack); + ASSERT_EQ(*p, 99); + + ret = stack_pop(stack, (void **)&p); + ASSERT_EQ(ret, 0); + ASSERT_EQ(*p, 99); + ASSERT_TRUE(stack_is_empty(stack)); + + stack_destroy(stack); + free(stack); + TEST_REPORT(); } blob - 8e29b506d9e134285c55909bb2bf72a0651d9280 blob + 3f0b829a6caea9cf9d0e8fbca2ddd54a7a8e8740 --- tests/test_string.c +++ tests/test_string.c @@ -5,7 +5,7 @@ #include "lfstring.h" int main() { - /* existing find_substrings tests */ + /* str_find tests */ const char *haystack = "Test one two one and also maybe two but not Gabe's least favorite number, which is not one."; const char *needles[] = { @@ -16,39 +16,81 @@ int main() { size_t sub_sz = 0; size_t *subs = NULL; - find_substrings(haystack, needles[0], &sub_sz, &subs); + str_find(haystack, needles[0], &sub_sz, &subs); ASSERT_EQ(sub_sz, 3); ASSERT_EQ(subs[0], 5); ASSERT_EQ(subs[1], 13); ASSERT_EQ(subs[2], 87); - char *s = substr(haystack, subs[0], strlen(needles[0])); + char *s = str_substr(haystack, subs[0], strlen(needles[0])); ASSERT_STR_EQ(s, needles[0]); free(s); free(subs); subs = NULL; - find_substrings(haystack, needles[1], &sub_sz, &subs); + str_find(haystack, needles[1], &sub_sz, &subs); ASSERT_EQ(sub_sz, 2); ASSERT_EQ(subs[0], 9); free(subs); subs = NULL; - find_substrings("test one two", "nope", &sub_sz, &subs); + str_find("test one two", "nope", &sub_sz, &subs); ASSERT_EQ(sub_sz, 0); ASSERT_NULL(subs); free(subs); subs = NULL; - find_substrings("123", "nopes", &sub_sz, &subs); + str_find("123", "nopes", &sub_sz, &subs); ASSERT_EQ(sub_sz, 0); ASSERT_NULL(subs); free(subs); subs = NULL; + /* str_find: empty needle matches every position */ + str_find("abc", "", &sub_sz, &subs); + ASSERT_EQ(sub_sz, 4); + free(subs); + subs = NULL; + + /* str_find: empty haystack with empty needle */ + str_find("", "", &sub_sz, &subs); + ASSERT_EQ(sub_sz, 1); + free(subs); + subs = NULL; + + /* str_find: empty haystack with non-empty needle */ + str_find("", "abc", &sub_sz, &subs); + ASSERT_EQ(sub_sz, 0); + ASSERT_NULL(subs); + subs = NULL; + + /* str_substr edge cases */ + s = str_substr("hello", 0, 5); + ASSERT_STR_EQ(s, "hello"); + free(s); + + s = str_substr("hello", 0, 0); + ASSERT_STR_EQ(s, ""); + free(s); + + s = str_substr("hello", 5, 0); + ASSERT_STR_EQ(s, ""); + free(s); + + s = str_substr("hello", 4, 1); + ASSERT_STR_EQ(s, "o"); + free(s); + + /* substr beyond bounds */ + s = str_substr("hello", 3, 5); + ASSERT_NULL(s); + + s = str_substr("hello", 0, 6); + ASSERT_NULL(s); + /* str_trim */ s = str_trim(" hello world "); ASSERT_STR_EQ(s, "hello world"); @@ -66,6 +108,10 @@ int main() { ASSERT_STR_EQ(s, "hi"); free(s); + s = str_trim(""); + ASSERT_STR_EQ(s, ""); + free(s); + /* str_join */ const char *parts[] = {"foo", "bar", "baz"}; s = str_join(parts, 3, ", "); @@ -89,6 +135,8 @@ int main() { ASSERT_TRUE(str_starts_with("hello", "hello")); ASSERT_FALSE(str_starts_with("hello", "world")); ASSERT_TRUE(str_starts_with("hello", "")); + ASSERT_TRUE(str_starts_with("", "")); + ASSERT_FALSE(str_starts_with("", "a")); /* str_ends_with */ ASSERT_TRUE(str_ends_with("hello world", "world")); @@ -96,6 +144,8 @@ int main() { ASSERT_FALSE(str_ends_with("hello", "world")); ASSERT_TRUE(str_ends_with("hello", "")); ASSERT_FALSE(str_ends_with("hi", "longer")); + ASSERT_TRUE(str_ends_with("", "")); + ASSERT_FALSE(str_ends_with("", "a")); /* str_replace */ s = str_replace("foo bar foo baz foo", "foo", "qux"); @@ -114,6 +164,16 @@ int main() { ASSERT_STR_EQ(s, ""); free(s); + /* str_replace with empty old string returns copy */ + s = str_replace("hello", "", "X"); + ASSERT_STR_EQ(s, "hello"); + free(s); + + /* str_replace on empty input */ + s = str_replace("", "a", "b"); + ASSERT_STR_EQ(s, ""); + free(s); + /* str_to_upper / str_to_lower */ s = str_to_upper("hello World 123"); ASSERT_STR_EQ(s, "HELLO WORLD 123"); @@ -123,5 +183,71 @@ int main() { ASSERT_STR_EQ(s, "hello world 123"); free(s); + s = str_to_upper(""); + ASSERT_STR_EQ(s, ""); + free(s); + + s = str_to_lower(""); + ASSERT_STR_EQ(s, ""); + free(s); + + /* str_split */ + int count = 0; + char **split = str_split("foo,bar,baz", ",", &count); + ASSERT_EQ(count, 3); + ASSERT_STR_EQ(split[0], "foo"); + ASSERT_STR_EQ(split[1], "bar"); + ASSERT_STR_EQ(split[2], "baz"); + for (int i = 0; i < count; i++) free(split[i]); + free(split); + + split = str_split("hello", ",", &count); + ASSERT_EQ(count, 1); + ASSERT_STR_EQ(split[0], "hello"); + free(split[0]); + free(split); + + split = str_split(",a,,b,", ",", &count); + ASSERT_EQ(count, 5); + ASSERT_STR_EQ(split[0], ""); + ASSERT_STR_EQ(split[1], "a"); + ASSERT_STR_EQ(split[2], ""); + ASSERT_STR_EQ(split[3], "b"); + ASSERT_STR_EQ(split[4], ""); + for (int i = 0; i < count; i++) free(split[i]); + free(split); + + /* str_split with multi-char delimiter */ + split = str_split("a::b::c", "::", &count); + ASSERT_EQ(count, 3); + ASSERT_STR_EQ(split[0], "a"); + ASSERT_STR_EQ(split[1], "b"); + ASSERT_STR_EQ(split[2], "c"); + for (int i = 0; i < count; i++) free(split[i]); + free(split); + + /* str_split with empty delimiter returns whole string */ + split = str_split("hello", "", &count); + ASSERT_EQ(count, 1); + ASSERT_STR_EQ(split[0], "hello"); + free(split[0]); + free(split); + + /* str_split empty string */ + split = str_split("", ",", &count); + ASSERT_EQ(count, 1); + ASSERT_STR_EQ(split[0], ""); + free(split[0]); + free(split); + + /* str_contains */ + ASSERT_TRUE(str_contains("hello world", "world")); + ASSERT_TRUE(str_contains("hello world", "hello")); + ASSERT_TRUE(str_contains("hello", "hello")); + ASSERT_FALSE(str_contains("hello", "xyz")); + ASSERT_TRUE(str_contains("hello", "")); + ASSERT_TRUE(str_contains("", "")); + ASSERT_FALSE(str_contains("", "a")); + TEST_REPORT(); } blob - 104b6f1acfc1c7a060396ba8ebc70626d3995941 blob + fa31ddfd0081614dd96eb80b4bc1b218634abf4c --- tests/test_vector.c +++ tests/test_vector.c @@ -3,7 +3,15 @@ #include "lftest.h" #include "lfvector.h" +static int destroy_count; + +static void counting_destroy(void *data) { + (void)data; + destroy_count++; +} + int main() { + /* --- existing tests --- */ Vector *v = malloc(sizeof(Vector)); vec_init(v, NULL); @@ -132,5 +140,98 @@ int main() { vec_destroy(v); free(v); + /* --- destroy callback --- */ + destroy_count = 0; + Vector dv; + vec_init(&dv, counting_destroy); + int a = 1, b = 2, c = 3; + vec_push(&dv, &a); + vec_push(&dv, &b); + vec_push(&dv, &c); + vec_destroy(&dv); + ASSERT_EQ(destroy_count, 3); + + /* --- vec_init_with_capacity --- */ + Vector cv; + ASSERT_EQ(vec_init_with_capacity(&cv, NULL, 16), 0); + ASSERT_EQ(vec_cap(&cv), 16); + ASSERT_EQ(vec_len(&cv), 0); + vec_destroy(&cv); + + /* --- vec_init NULL check --- */ + ASSERT_EQ(vec_init(NULL, NULL), -1); + ASSERT_EQ(vec_init_with_capacity(NULL, NULL, 4), -1); + + /* --- insert at index 0 --- */ + Vector iv; + vec_init(&iv, NULL); + int x1 = 10, x2 = 20, x3 = 30; + vec_push(&iv, &x1); + vec_push(&iv, &x2); + vec_insert(&iv, &x3, 0); + ASSERT_EQ(*(int *)vec_at(&iv, 0), 30); + ASSERT_EQ(*(int *)vec_at(&iv, 1), 10); + ASSERT_EQ(*(int *)vec_at(&iv, 2), 20); + ASSERT_EQ(vec_len(&iv), 3); + + /* --- insert out of bounds --- */ + ASSERT_EQ(vec_insert(&iv, &x1, 100), -1); + + vec_destroy(&iv); + + /* --- vec_sort empty --- */ + Vector sv; + vec_init(&sv, NULL); + vec_sort(&sv, vec_cmp_int); + ASSERT_EQ(vec_len(&sv), 0); + + /* --- vec_sort single element --- */ + int ss1 = 42; + vec_push(&sv, &ss1); + vec_sort(&sv, vec_cmp_int); + ASSERT_EQ(vec_len(&sv), 1); + ASSERT_EQ(*(int *)vec_at(&sv, 0), 42); + + /* --- vec_sort multiple elements --- */ + int ss2 = 5, ss3 = 99, ss4 = 1; + vec_push(&sv, &ss2); + vec_push(&sv, &ss3); + vec_push(&sv, &ss4); + vec_sort(&sv, vec_cmp_int); + ASSERT_EQ(*(int *)vec_at(&sv, 0), 1); + ASSERT_EQ(*(int *)vec_at(&sv, 1), 5); + ASSERT_EQ(*(int *)vec_at(&sv, 2), 42); + ASSERT_EQ(*(int *)vec_at(&sv, 3), 99); + vec_destroy(&sv); + + /* --- vec_shrink on empty vector --- */ + Vector ev; + vec_init(&ev, NULL); + ASSERT_EQ(vec_shrink(&ev), 0); + ASSERT_EQ(vec_cap(&ev), 0); + ASSERT_NULL(ev.elements); + + /* --- vec_clear return check --- */ + Vector clv; + vec_init(&clv, NULL); + int cl1 = 1; + vec_push(&clv, &cl1); + ASSERT_EQ(vec_clear(&clv), 0); + ASSERT_EQ(vec_len(&clv), 0); + vec_destroy(&clv); + + /* --- vec_remove out of bounds --- */ + Vector rv; + vec_init(&rv, NULL); + ASSERT_NULL(vec_remove(&rv, 0)); + ASSERT_NULL(vec_safe_at(&rv, 0)); + vec_destroy(&rv); + + /* --- vec_grow_to smaller fails --- */ + Vector gv; + vec_init(&gv, NULL); + ASSERT_EQ(vec_grow_to(&gv, 1), -1); + vec_destroy(&gv); + TEST_REPORT(); } blob - /dev/null blob + 063e3213f5fbb2126021a6460d57417a91325581 (mode 644) --- /dev/null +++ tests/test_utility.c @@ -0,0 +1,47 @@ +#include +#include +#include "lftest.h" +#include "lfutility.h" + +int main() { + /* Point_new */ + Point p = Point_new(3, 7); + ASSERT_EQ(p.x, 3); + ASSERT_EQ(p.y, 7); + + /* Point_new_p */ + Point *pp = Point_new_p(5, 11); + ASSERT_NOT_NULL(pp); + ASSERT_EQ(pp->x, 5); + ASSERT_EQ(pp->y, 11); + + /* Point_eq */ + Point a = Point_new(1, 2); + Point b = Point_new(1, 2); + Point c = Point_new(3, 4); + ASSERT_TRUE(Point_eq(a, b)); + ASSERT_FALSE(Point_eq(a, c)); + + /* Point_cmp_p */ + Point *pa = Point_new_p(1, 2); + Point *pb = Point_new_p(1, 2); + Point *pc = Point_new_p(9, 9); + ASSERT_NOT_NULL(pa); + ASSERT_NOT_NULL(pb); + ASSERT_NOT_NULL(pc); + ASSERT_TRUE(Point_cmp_p(pa, pb)); + ASSERT_FALSE(Point_cmp_p(pa, pc)); + + /* Point_cmp_v */ + ASSERT_TRUE(Point_cmp_v(pa, pb)); + ASSERT_FALSE(Point_cmp_v(pa, pc)); + + /* Point_destroy */ + Point_destroy(pp); + Point_destroy(pa); + Point_destroy(pb); + Point_destroy(pc); + Point_destroy(NULL); /* should not crash */ + + TEST_REPORT(); +}