Compare commits

...

7 Commits

Author SHA1 Message Date
Aryadev Chavali
97403121a3 prick_btree: simplicity is king
no need for helpers, just force the user to pass in the relevant
components when required.
2026-01-22 18:10:42 +00:00
Aryadev Chavali
00d4ebe4a4 prick_darr: hard fail when unable to allocate resources
So if calloc fails, hard exit with a message.
tbh if you're working on something and I run out of memory here, I'm
doing you a favour.
2025-12-12 01:33:25 +00:00
Aryadev Chavali
aaa074912f Add prick.org for tasks 2025-12-11 21:13:33 +00:00
Aryadev Chavali
f93f0d46ae Indentation 2025-12-11 21:13:27 +00:00
Aryadev Chavali
9a62cd34f4 Actually use the custom print function in prick_bnode_print for value 2025-12-11 21:11:56 +00:00
Aryadev Chavali
69ec22ae74 prick_darr: adjust commentary 2025-12-11 21:08:05 +00:00
Aryadev Chavali
2e3742c190 prick_darr: namespace all functions and structures with prick_ 2025-12-11 20:49:12 +00:00
4 changed files with 122 additions and 130 deletions

18
prick.org Normal file
View File

@@ -0,0 +1,18 @@
#+title: Prick tasks
#+author: Aryadev Chavali
#+date: 2025-12-11
#+filetags: c prick
* prick_darr :prick_darr:
** TODO Use custom allocator
Allow users to provide custom allocator functions. They need to
provide:
- alloc
- realloc
- free
* prick_btree :prick_btree:
** TODO Pack custom user functions into their own structure
The allocation/print routines should be in their own structure which
the user passes in.
This means we don't need to have a prick_btree_t structure at all.

View File

@@ -41,4 +41,4 @@ typedef double f64;
* which you should have received a copy of along with this program. If not, * which you should have received a copy of along with this program. If not,
* please go to <https://unlicense.org/>. * please go to <https://unlicense.org/>.
*/ */

View File

@@ -24,79 +24,50 @@ typedef struct Prick_Bnode
struct Prick_Bnode *left, *right; struct Prick_Bnode *left, *right;
} prick_bnode_t; } prick_bnode_t;
typedef int (*prick_bnode_comp_fn)(void *, void *); typedef int (*prick_btree_comp_fn)(void *, void *);
typedef prick_bnode_t *(*prick_bnode_alloc_fn)(); typedef prick_bnode_t *(*prick_btree_alloc_fn)();
typedef void (*prick_bnode_free_fn)(prick_bnode_t *); typedef void (*prick_btree_free_fn)(prick_bnode_t *);
typedef void (*prick_print_fn)(FILE *, void *); typedef void (*prick_btree_print_fn)(FILE *, void *);
typedef struct prick_bnode_t *prick_btree_insert(prick_bnode_t *node, void *value,
{ prick_btree_comp_fn comp,
prick_bnode_t *root; prick_btree_alloc_fn alloc);
prick_bnode_comp_fn comp; void prick_btree_right_rotate(prick_bnode_t **node);
prick_bnode_alloc_fn alloc; void prick_btree_left_rotate(prick_bnode_t **node);
prick_bnode_free_fn free; void prick_btree_print(prick_bnode_t *root, FILE *fp,
prick_print_fn print; prick_btree_print_fn print);
} prick_btree_t; void prick_btree_free(prick_bnode_t *node, prick_btree_free_fn free);
void prick_btree_init(prick_btree_t *tree, prick_bnode_comp_fn comparator,
prick_bnode_alloc_fn allocator, prick_bnode_free_fn free,
prick_print_fn print);
prick_bnode_t *prick_btree_insert(prick_btree_t *tree, void *value);
void prick_btree_print(FILE *fp, prick_btree_t *tree);
void prick_btree_free(prick_btree_t *tree);
void prick_bnode_right_rotate(prick_bnode_t **node);
void prick_bnode_left_rotate(prick_bnode_t **node);
void prick_bnode_print(FILE *fp, prick_print_fn print, prick_bnode_t *root);
#ifdef PRICK_BTREE_IMPL #ifdef PRICK_BTREE_IMPL
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
void prick_btree_init(prick_btree_t *tree, prick_bnode_comp_fn comparator, prick_bnode_t *prick_btree_insert(prick_bnode_t *node, void *value,
prick_bnode_alloc_fn allocator, prick_bnode_free_fn free, prick_btree_comp_fn comp,
prick_print_fn print) prick_btree_alloc_fn alloc)
{
// NOTE: These NEED to be supplied.
assert(comparator);
assert(allocator);
assert(free);
assert(print);
if (tree)
{
tree->root = NULL;
tree->comp = comparator;
tree->alloc = allocator;
tree->free = free;
tree->print = print;
}
}
prick_bnode_t *prick_bnode_insert(prick_bnode_t *node, prick_btree_t *tree,
void *value)
{ {
if (!node) if (!node)
{ {
node = tree->alloc(); node = alloc();
node->value = value; node->value = value;
node->left = NULL; node->left = NULL;
node->right = NULL; node->right = NULL;
return node; return node;
} }
int comp = tree->comp(value, node->value); int comparison = comp(value, node->value);
prick_bnode_t **picked_node = NULL; prick_bnode_t **picked_node = NULL;
if (comp < 0) if (comparison < 0)
picked_node = &node->left; picked_node = &node->left;
else else
picked_node = &node->right; picked_node = &node->right;
if (*picked_node) if (*picked_node)
prick_bnode_insert(*picked_node, tree, value); prick_btree_insert(*picked_node, value, comp, alloc);
else else
{ {
*picked_node = tree->alloc(); *picked_node = alloc();
picked_node[0]->value = value; picked_node[0]->value = value;
picked_node[0]->left = NULL; picked_node[0]->left = NULL;
picked_node[0]->right = NULL; picked_node[0]->right = NULL;
@@ -105,25 +76,7 @@ prick_bnode_t *prick_bnode_insert(prick_bnode_t *node, prick_btree_t *tree,
return node; return node;
} }
prick_bnode_t *prick_btree_insert(prick_btree_t *tree, void *value) void prick_btree_free(prick_bnode_t *bnode, prick_btree_free_fn free_fn)
{
tree->root = prick_bnode_insert(tree->root, tree, value);
return tree->root;
}
void prick_btree_print(FILE *fp, prick_btree_t *tree)
{
if (!tree->root)
{
fprintf(fp, "()");
}
else
{
prick_bnode_print(fp, tree->print, tree->root);
}
}
void prick_bnode_free(prick_bnode_t *bnode, prick_bnode_free_fn free_fn)
{ {
if (!bnode) if (!bnode)
return; return;
@@ -131,19 +84,11 @@ void prick_bnode_free(prick_bnode_t *bnode, prick_bnode_free_fn free_fn)
prick_bnode_t *right = bnode->right; prick_bnode_t *right = bnode->right;
free_fn(bnode); free_fn(bnode);
prick_bnode_free(left, free_fn); prick_btree_free(left, free_fn);
prick_bnode_free(right, free_fn); prick_btree_free(right, free_fn);
} }
void prick_btree_free(prick_btree_t *tree) void prick_btree_right_rotate(prick_bnode_t **node)
{
if (!tree)
return;
prick_bnode_free(tree->root, tree->free);
tree->root = NULL;
}
void prick_bnode_right_rotate(prick_bnode_t **node)
{ {
if (!node || !*node) if (!node || !*node)
return; return;
@@ -157,7 +102,7 @@ void prick_bnode_right_rotate(prick_bnode_t **node)
} }
} }
void prick_bnode_left_rotate(prick_bnode_t **node) void prick_btree_left_rotate(prick_bnode_t **node)
{ {
if (!node || !*node) if (!node || !*node)
return; return;
@@ -171,22 +116,26 @@ void prick_bnode_left_rotate(prick_bnode_t **node)
} }
} }
void prick_bnode_print(FILE *fp, prick_print_fn print, prick_bnode_t *root) void prick_btree_print(prick_bnode_t *root, FILE *fp,
prick_btree_print_fn print)
{ {
if (!root) if (!root)
{
fprintf(fp, "()");
return; return;
}
fprintf(fp, "("); fprintf(fp, "(");
print(fp, root->value); print(fp, root->value);
if (root->left) if (root->left)
{ {
fprintf(fp, " l"); fprintf(fp, " l");
prick_bnode_print(fp, print, root->left); prick_btree_print(root->left, fp, print);
} }
if (root->right) if (root->right)
{ {
fprintf(fp, " r"); fprintf(fp, " r");
prick_bnode_print(fp, print, root->right); prick_btree_print(root->right, fp, print);
} }
fprintf(fp, ")"); fprintf(fp, ")");
@@ -207,4 +156,4 @@ void prick_bnode_print(FILE *fp, prick_print_fn print, prick_bnode_t *root)
* Unlicense, which you should have received a copy of along with this * Unlicense, which you should have received a copy of along with this
* program. If not, please go to <https://unlicense.org/>. * program. If not, please go to <https://unlicense.org/>.
*/ */

View File

@@ -9,16 +9,16 @@
#include "prick_darr.h" #include "prick_darr.h"
in one of your code units. in one of your code units.
This library defines a dynamic array purely on the heap. We split the one This library defines a dynamic array purely on the heap. Both the raw data for
dynamic array allocation into two parts: the metadata, and the actual data. the array as well as the metadata are in one allocation. Consumers of the
Consumers of the library will only ever need to deal with the latter component library will only ever need to deal with the former component i.e. they'll only
i.e. they'll only ever have access to the data they require. deal with the raw data pointer.
Unfortuntely this does mean that the underlying data pointer is _not_ stable Unfortuntely this does mean that the overall pointer to the vector is _not_
during capacity reallocation, as we're allocating a whole new pointer with the stable during capacity reallocation, as on reallocation we allocate a whole new
correct size. Therefore, if you're expecting the array to grow during the area on the heap and copy over the data. Therefore, if you're expecting the
runtime of your program, you should ensure any pointers to components of it are array to grow during the runtime of your program, you should ensure any
updated as they will otherwise be dangling. pointers to components of it are updated as they will otherwise be dangling.
*/ */
#ifndef PRICK_DARR_H #ifndef PRICK_DARR_H
@@ -30,89 +30,114 @@ typedef struct
{ {
uint32_t size, capacity; uint32_t size, capacity;
uint8_t bytes[]; uint8_t bytes[];
} darr_t; } prick_darr_t;
#define DARR_GET(P) (((darr_t *)(P)) - 1) #define PRICK_DARR_GET(P) (((prick_darr_t *)(P)) - 1)
#define DARR_SIZE(P) (DARR_GET(P)->size) #define PRICK_DARR_SIZE(P) (PRICK_DARR_GET(P)->size)
#define DARR_CAP(P) (DARR_GET(P)->capacity) #define PRICK_DARR_CAP(P) (PRICK_DARR_GET(P)->capacity)
void darr_make(void **ptr, uint32_t size); void prick_darr_make(void **ptr, uint32_t size);
void darr_free(void **ptr); void prick_darr_free(void **ptr);
void darr_append_byte(void **ptr, uint8_t byte); void prick_darr_append_byte(void **ptr, uint8_t byte);
void darr_append(void **ptr, void *data, uint32_t size); void prick_darr_append(void **ptr, void *data, uint32_t size);
void darr_clone(void **dest, void **src); void prick_darr_clone(void **dest, void **src);
void darr_ensure_remaining(void **ptr, uint32_t space); void prick_darr_ensure_remaining(void **ptr, uint32_t space);
#ifdef PRICK_DARR_IMPL #ifdef PRICK_DARR_IMPL
#include <malloc.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#ifndef DARR_MULT #ifndef PRICK_DARR_MULT
#define DARR_MULT 2 #define PRICK_DARR_MULT 2
#endif #endif
#define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define FAIL(...) \
do \
{ \
fprintf(stderr, "FAIL: prick_darr: "); \
fprintf(stderr, __VA_ARGS__); \
exit(1); \
} while (0)
void darr_make(void **ptr, uint32_t size) void prick_darr_make(void **ptr, uint32_t size)
{ {
if (!ptr) if (!ptr)
return; return;
darr_t *darrtor = calloc(1, sizeof(*darrtor) + size); prick_darr_t *darr = calloc(1, sizeof(*darr) + size);
darrtor->size = 0; if (!darr)
darrtor->capacity = size; FAIL("Could not allocate memory (%lu bytes) for new dynamic array.\n",
*ptr = (darrtor + 1); sizeof(*darr) + size);
darr->size = 0;
darr->capacity = size;
*ptr = (darr + 1);
} }
void darr_free(void **data) void prick_darr_free(void **data)
{ {
if (!data || !*data) if (!data || !*data)
return; return;
free(DARR_GET(*data)); free(PRICK_DARR_GET(*data));
*data = NULL; *data = NULL;
} }
void darr_ensure_remaining(void **ptr, uint32_t space) void prick_darr_ensure_remaining(void **ptr, uint32_t space)
{ {
if (!ptr || !*ptr) if (!ptr || !*ptr)
return; return;
darr_t *darr = DARR_GET(*ptr); prick_darr_t *darr = PRICK_DARR_GET(*ptr);
if (darr->capacity - darr->size < space) if (darr->capacity - darr->size < space)
{ {
void *new_darr = NULL; void *new_darr = NULL;
darr_make(&new_darr, MAX(darr->capacity * DARR_MULT, darr->size + space)); uint64_t new_capacity =
DARR_SIZE(new_darr) = darr->size; MAX(darr->capacity * PRICK_DARR_MULT, darr->size + space);
prick_darr_make(&new_darr, new_capacity);
if (!new_darr)
FAIL("Could not allocate new dynamic array with %lu bytes capacity.\n",
new_capacity);
PRICK_DARR_SIZE(new_darr) = darr->size;
memcpy(new_darr, *ptr, darr->size); memcpy(new_darr, *ptr, darr->size);
darr_free(ptr); prick_darr_free(ptr);
*ptr = new_darr; *ptr = new_darr;
} }
} }
void darr_append_byte(void **ptr, uint8_t byte) void prick_darr_append_byte(void **ptr, uint8_t byte)
{ {
darr_ensure_remaining(ptr, 1); if (!ptr || !*ptr)
darr_t *darr = DARR_GET(*ptr); return;
prick_darr_ensure_remaining(ptr, 1);
prick_darr_t *darr = PRICK_DARR_GET(*ptr);
darr->bytes[darr->size++] = byte; darr->bytes[darr->size++] = byte;
} }
void darr_append(void **ptr, void *data, uint32_t size) void prick_darr_append(void **ptr, void *data, uint32_t size)
{ {
darr_ensure_remaining(ptr, size); if (!ptr || !*ptr || !data)
darr_t *darr = DARR_GET(*ptr); return;
memcpy(*ptr + darr->size, data, size); prick_darr_ensure_remaining(ptr, size);
prick_darr_t *darr = PRICK_DARR_GET(*ptr);
memcpy(((uint8_t *)*ptr) + darr->size, data, size);
darr->size += size; darr->size += size;
} }
void darr_clone(void **dest, void **src) void prick_darr_clone(void **dest, void **src)
{ {
if (!dest || !src || !*src) if (!dest || !src || !*src)
return; return;
darr_make(dest, DARR_SIZE(*src)); prick_darr_make(dest, PRICK_DARR_SIZE(*src));
memcpy(*dest, *src, DARR_SIZE(*src)); memcpy(*dest, *src, PRICK_DARR_SIZE(*src));
DARR_SIZE(*dest) = DARR_SIZE(*src); PRICK_DARR_SIZE(*dest) = PRICK_DARR_SIZE(*src);
} }
#undef MAX
#undef MIN
#endif #endif
#endif #endif