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

@@ -24,79 +24,50 @@ typedef struct Prick_Bnode
struct Prick_Bnode *left, *right;
} prick_bnode_t;
typedef int (*prick_bnode_comp_fn)(void *, void *);
typedef prick_bnode_t *(*prick_bnode_alloc_fn)();
typedef void (*prick_bnode_free_fn)(prick_bnode_t *);
typedef void (*prick_print_fn)(FILE *, void *);
typedef int (*prick_btree_comp_fn)(void *, void *);
typedef prick_bnode_t *(*prick_btree_alloc_fn)();
typedef void (*prick_btree_free_fn)(prick_bnode_t *);
typedef void (*prick_btree_print_fn)(FILE *, void *);
typedef struct
{
prick_bnode_t *root;
prick_bnode_comp_fn comp;
prick_bnode_alloc_fn alloc;
prick_bnode_free_fn free;
prick_print_fn print;
} prick_btree_t;
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);
prick_bnode_t *prick_btree_insert(prick_bnode_t *node, void *value,
prick_btree_comp_fn comp,
prick_btree_alloc_fn alloc);
void prick_btree_right_rotate(prick_bnode_t **node);
void prick_btree_left_rotate(prick_bnode_t **node);
void prick_btree_print(prick_bnode_t *root, FILE *fp,
prick_btree_print_fn print);
void prick_btree_free(prick_bnode_t *node, prick_btree_free_fn free);
#ifdef PRICK_BTREE_IMPL
#include <assert.h>
#include <stdlib.h>
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)
{
// 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)
prick_bnode_t *prick_btree_insert(prick_bnode_t *node, void *value,
prick_btree_comp_fn comp,
prick_btree_alloc_fn alloc)
{
if (!node)
{
node = tree->alloc();
node = alloc();
node->value = value;
node->left = NULL;
node->right = NULL;
return node;
}
int comp = tree->comp(value, node->value);
int comparison = comp(value, node->value);
prick_bnode_t **picked_node = NULL;
if (comp < 0)
if (comparison < 0)
picked_node = &node->left;
else
picked_node = &node->right;
if (*picked_node)
prick_bnode_insert(*picked_node, tree, value);
prick_btree_insert(*picked_node, value, comp, alloc);
else
{
*picked_node = tree->alloc();
*picked_node = alloc();
picked_node[0]->value = value;
picked_node[0]->left = 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;
}
prick_bnode_t *prick_btree_insert(prick_btree_t *tree, void *value)
{
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)
void prick_btree_free(prick_bnode_t *bnode, prick_btree_free_fn free_fn)
{
if (!bnode)
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;
free_fn(bnode);
prick_bnode_free(left, free_fn);
prick_bnode_free(right, free_fn);
prick_btree_free(left, free_fn);
prick_btree_free(right, free_fn);
}
void prick_btree_free(prick_btree_t *tree)
{
if (!tree)
return;
prick_bnode_free(tree->root, tree->free);
tree->root = NULL;
}
void prick_bnode_right_rotate(prick_bnode_t **node)
void prick_btree_right_rotate(prick_bnode_t **node)
{
if (!node || !*node)
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)
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)
{
fprintf(fp, "()");
return;
}
fprintf(fp, "(");
print(fp, root->value);
if (root->left)
{
fprintf(fp, " l");
prick_bnode_print(fp, print, root->left);
prick_btree_print(root->left, fp, print);
}
if (root->right)
{
fprintf(fp, " r");
prick_bnode_print(fp, print, root->right);
prick_btree_print(root->right, fp, print);
}
fprintf(fp, ")");

View File

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