Compare commits

...

5 Commits

Author SHA1 Message Date
Aryadev Chavali
3ca2f3fb2a prick_btree: provide a general btree_t structure
This structure stores our comparator and allocator functions, as well
as a root node, ensuring we're using the same functions on one tree.
Makes the API much cleaner to use.
2025-11-17 01:05:09 +00:00
Aryadev Chavali
6d91af65aa prick_btree
Still need to make it better imo
2025-11-17 00:56:28 +00:00
Aryadev Chavali
cb583d0277 types -> prick_aliases 2025-11-17 00:56:19 +00:00
Aryadev Chavali
50bd444d7e arena -> prick_arena 2025-11-17 00:56:19 +00:00
Aryadev Chavali
f4364f38d1 vec -> prick_darr.h 2025-11-17 00:56:19 +00:00
5 changed files with 374 additions and 207 deletions

View File

@@ -1,20 +1,17 @@
/* Copyright (C) 2025 Aryadev Chavali
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the Unlicense for details.
* You may distribute and modify this code under the terms of the Unlicense,
* which you should have received a copy of along with this program. If not,
* please go to <https://unlicense.org/>.
/* prick_aliases.h:
* Created: 2025-04-09
* Description: Some basic type definitions to make life easier.
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
This library defines some useful aliases for common types. These are mostly
handpicked, and aren't necessary.
*/
#ifndef TYPES_H
#define TYPES_H
#ifndef PRICK_ALIASES_H
#define PRICK_ALIASES_H
#include <assert.h>
#include <stdint.h>
typedef uint8_t u8;
@@ -27,4 +24,21 @@ typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
static_assert(sizeof(float) == 4, "f32 requires 4 byte floats");
static_assert(sizeof(double) == 8, "f64 requires 8 byte doubles");
typedef float f32;
typedef double f64;
#endif
/* Copyright (C) 2025 Aryadev Chavali
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the Unlicense for details.
* You may distribute and modify this code under the terms of the Unlicense,
* which you should have received a copy of along with this program. If not,
* please go to <https://unlicense.org/>.
*/

View File

@@ -1,30 +1,30 @@
/* Copyright (C) 2024 Aryadev Chavali
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense
* for details.
* You may distribute and modify this code under the terms of the
* Unlicense, which you should have received a copy of along with this
* program. If not, please go to <https://unlicense.org/>.
/* prick_arena.h: An arena implementation.
* Created: 2024-11-01
* Author: Aryadev Chavali
* Description: Arena allocator.
* License: see end of file
* Commentary:
To utilise this library, please put:
#define PRICK_ARENA_IMPL
#include "prick_arena.h"
in one of your code units.
This library defines both:
- A simple bump allocator for regions, with the ability to attach more regions
via a linked list in case the current region when the current region is full.
- A simple arena memory allocator, using the bump allocator for its regions.
The regions of the arena are arranged in a linked list for simplicity. As
regions aren't reallocated (instead, a new region is generated), they are
stable pointers, and may be used throughout a program as long as the underlying
arena isn't deleted.
*/
#ifndef ARENA_H
#define ARENA_H
#ifndef PRICK_ARENA_H
#define PRICK_ARENA_H
#include <stdint.h>
/**
@brief A single block of memory to be used by an arena.
@details Blocks of memory arranged in a singly linked list.
Each individual node is a bump allocator.
*/
typedef struct Region
{
struct Region *next;
@@ -32,38 +32,9 @@ typedef struct Region
uint8_t bytes[];
} region_t;
/**
@brief Allocate a new region on the heap with requested size and a pointer to
the next region.
@details If capacity is less than REGION_DEFAULT_SIZE, capacity is set to
REGION_DEFAULT_SIZE.
*/
region_t *region_make(uint32_t capacity, region_t *next);
/**
@brief Allocate memory of requested size on the region.
@details If the region cannot fit the requested size, then return NULL.
Otherwise return a pointer to the start of the allocated memory, incrementing
the region size appropriately.
*/
uint8_t *region_alloc_flat(region_t *region, uint32_t size);
/**
@brief Allocate memory of requested size on the region.
@details Iterates through the linked list of regions to find an appropriately
sized region for the requested size, allocating a new region if one cannot be
found. This new region will have capacity at least size *
REGION_CAPACITY_MULT.
Returns a pointer to the start of the allocated memory, incrementing the
appropriate region's size.
*/
uint8_t *region_alloc_rec(region_t *region, uint32_t size);
/**
@brief Delete a region, freeing its memory.
@details Will free all regions following it in the linked list.
*/
void region_delete_rec(region_t *region);
typedef struct
@@ -71,46 +42,13 @@ typedef struct
region_t *beg, *end;
} arena_t;
/**
@brief Allocate memory of requested size in the arena, returning a pointer to
the start of it.
@details Uses region_alloc_rec internally to allocate the memory required.
arena->beg and arena->end are set appropriately for this task.
*/
uint8_t *arena_alloc(arena_t *arena, uint32_t size);
/**
@brief Reallocate buffer of old_size to a buffer of new_size in the
arena, returning a pointer to the start of the new buffer.
@details If the pointer is not allocated in the arena, return NULL. If the
pointer and old_size cover a complete region reallocate the region itself to
fit the newly requested size, relinking it in the linked list. Otherwise,
allocate as per usual.
The contents of the old memory are copied into the new buffer. If old_size >
new_size, only new_size bytes will be copied from the old buffer into the new
one.
*/
uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size,
uint32_t new_size);
/**
@brief Reset the arena for reuse.
@details Sets all regions to default values, setting size to 0. No memory is
deleted in this operation.
*/
void arena_reset(arena_t *arena);
/**
@brief Free the memory associated with the arena.
@details Deletes all regions of memory associated in the arena.
*/
void arena_free(arena_t *arena);
#ifdef ARENA_IMPL
#ifdef PRICK_ARENA_IMPL
#include <malloc.h>
#include <string.h>
@@ -239,3 +177,15 @@ void arena_free(arena_t *arena)
#endif
#endif
/* Copyright (C) 2024 Aryadev Chavali
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense
* for details.
* You may distribute and modify this code under the terms of the
* Unlicense, which you should have received a copy of along with this
* program. If not, please go to <https://unlicense.org/>.
*/

184
prick_btree.h Normal file
View File

@@ -0,0 +1,184 @@
/* prick_btree.h: A generic ordered binary tree.
* Created: 2025-04-09
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
To utilise this library, please put:
#define PRICK_BTREE_IMPL
#include "prick_btree.h"
in one of your code units.
An ordered binary tree implementation, allowing the use of custom comparators
and allocators.
*/
#ifndef PRICK_BTREE_H
#define PRICK_BTREE_H
#include <stdio.h>
typedef struct BNode
{
void *value;
struct BNode *left, *right;
} bnode_t;
typedef int (*bnode_comp_fn)(void *, void *);
typedef bnode_t *(*bnode_alloc_fn)();
typedef void (*bnode_free_fn)(bnode_t *);
typedef struct
{
bnode_t *root;
bnode_comp_fn comp;
bnode_alloc_fn alloc;
bnode_free_fn free;
} btree_t;
void btree_init(btree_t *tree, bnode_comp_fn comparator,
bnode_alloc_fn allocator, bnode_free_fn free);
bnode_t *btree_insert(btree_t *tree, void *value);
void btree_free(btree_t *tree);
void bnode_right_rotate(bnode_t **node);
void bnode_left_rotate(bnode_t **node);
void bnode_print(FILE *fp, bnode_t *root);
#ifdef PRICK_BTREE_IMPL
#include <stdlib.h>
void btree_init(btree_t *tree, bnode_comp_fn comparator,
bnode_alloc_fn allocator, bnode_free_fn free)
{
if (tree)
{
tree->root = NULL;
tree->comp = comparator;
tree->alloc = allocator;
tree->free = free;
}
}
bnode_t *bnode_insert(bnode_t *node, btree_t *tree, void *value)
{
if (!node)
{
node = tree->alloc();
node->value = value;
node->left = NULL;
node->right = NULL;
return node;
}
int comp = tree->comp(value, node->value);
bnode_t **picked_node = NULL;
if (comp < 0)
picked_node = &node->left;
else
picked_node = &node->right;
if (*picked_node)
bnode_insert(*picked_node, tree, value);
else
{
*picked_node = tree->alloc();
picked_node[0]->value = value;
picked_node[0]->left = NULL;
picked_node[0]->right = NULL;
}
return node;
}
bnode_t *btree_insert(btree_t *tree, void *value)
{
tree->root = bnode_insert(tree->root, tree, value);
return tree->root;
}
void bnode_free(bnode_t *bnode, bnode_free_fn free_fn)
{
if (!bnode)
return;
bnode_t *left = bnode->left;
bnode_t *right = bnode->right;
free_fn(bnode);
bnode_free(left, free_fn);
bnode_free(right, free_fn);
}
void btree_free(btree_t *tree)
{
if (!tree)
return;
bnode_free(tree->root, tree->free);
tree->root = NULL;
}
void bnode_right_rotate(bnode_t **node)
{
if (!node || !*node)
return;
bnode_t *left = (*node)->left;
if (left)
{
(*node)->left = left->right;
left->right = *node;
*node = left;
}
}
void bnode_left_rotate(bnode_t **node)
{
if (!node || !*node)
return;
bnode_t *right = (*node)->right;
if (right)
{
(*node)->right = right->left;
right->left = *node;
*node = right;
}
}
void bnode_print(FILE *fp, bnode_t *root)
{
if (!root)
return;
fprintf(fp, "(%p", root->value);
if (root->left)
{
fprintf(fp, " ");
bnode_print(fp, root->left);
}
if (root->right)
{
fprintf(fp, " ");
bnode_print(fp, root->right);
}
fprintf(fp, ")");
}
#endif
#endif
/* Copyright (C) 2025 Aryadev Chavali
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense
* for details.
* You may distribute and modify this code under the terms of the
* Unlicense, which you should have received a copy of along with this
* program. If not, please go to <https://unlicense.org/>.
*/

130
prick_darr.h Normal file
View File

@@ -0,0 +1,130 @@
/* prick_darr.h: A dynamically sized array, all on the heap.
* Created: 2024-10-01
* Author: Aryadev Chavali
* License: see end of file
* Commentary:
To utilise this library, please put:
#define PRICK_DARR_IMPL
#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.
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.
*/
#ifndef PRICK_DARR_H
#define PRICK_DARR_H
#include <stdint.h>
typedef struct
{
uint32_t size, capacity;
uint8_t bytes[];
} 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)
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);
#ifdef PRICK_DARR_IMPL
#include <malloc.h>
#include <string.h>
#ifndef DARR_MULT
#define DARR_MULT 2
#endif
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
void 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);
}
void darr_free(void **data)
{
if (!data || !*data)
return;
free(DARR_GET(*data));
*data = NULL;
}
void darr_ensure_remaining(void **ptr, uint32_t space)
{
if (!ptr || !*ptr)
return;
darr_t *darr = 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;
memcpy(new_darr, *ptr, darr->size);
darr_free(ptr);
*ptr = new_darr;
}
}
void darr_append_byte(void **ptr, uint8_t byte)
{
darr_ensure_remaining(ptr, 1);
darr_t *darr = DARR_GET(*ptr);
darr->bytes[darr->size++] = byte;
}
void 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);
darr->size += size;
}
void 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);
}
#endif
#endif
/* Copyright (C) 2024 Aryadev Chavali
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense
* for details.
* You may distribute and modify this code under the terms of the
* Unlicense, which you should have received a copy of along with this
* program. If not, please go to <https://unlicense.org/>.
*/

111
vec.h
View File

@@ -1,111 +0,0 @@
/* Copyright (C) 2024 Aryadev Chavali
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense
* for details.
* You may distribute and modify this code under the terms of the
* Unlicense, which you should have received a copy of along with this
* program. If not, please go to <https://unlicense.org/>.
* Created: 2024-10-01
* Author: Aryadev Chavali
* Description: A dynamically sized container.
*/
#ifndef VEC_H
#define VEC_H
#include <stdint.h>
typedef struct
{
uint32_t size, capacity;
uint8_t bytes[];
} vec_t;
#define VEC_GET(P) (((vec_t *)(P)) - 1)
#define VEC_SIZE(P) (VEC_GET(P)->size)
#define VEC_CAP(P) (VEC_GET(P)->capacity)
void vec_make(void **ptr, uint32_t size);
void vec_free(void **ptr);
void vec_append_byte(void **ptr, uint8_t byte);
void vec_append(void **ptr, void *data, uint32_t size);
void vec_clone(void **dest, void **src);
void vec_ensure_remaining(void **ptr, uint32_t space);
#ifdef VEC_IMPL
#include <malloc.h>
#include <string.h>
#ifndef VEC_MULT
#define VEC_MULT 2
#endif
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
void vec_make(void **ptr, uint32_t size)
{
if (!ptr)
return;
vec_t *vector = calloc(1, sizeof(*vector) + size);
vector->size = 0;
vector->capacity = size;
*ptr = (vector + 1);
}
void vec_free(void **data)
{
if (!data || !*data)
return;
free(VEC_GET(*data));
*data = NULL;
}
void vec_ensure_remaining(void **ptr, uint32_t space)
{
if (!ptr || !*ptr)
return;
vec_t *vec = VEC_GET(*ptr);
if (vec->capacity - vec->size < space)
{
void *new_vec = NULL;
vec_make(&new_vec, MAX(vec->capacity * VEC_MULT, vec->size + space));
VEC_SIZE(new_vec) = vec->size;
memcpy(new_vec, *ptr, vec->size);
vec_free(ptr);
*ptr = new_vec;
}
}
void vec_append_byte(void **ptr, uint8_t byte)
{
vec_ensure_remaining(ptr, 1);
vec_t *vec = VEC_GET(*ptr);
vec->bytes[vec->size++] = byte;
}
void vec_append(void **ptr, void *data, uint32_t size)
{
vec_ensure_remaining(ptr, size);
vec_t *vec = VEC_GET(*ptr);
memcpy(*ptr + vec->size, data, size);
vec->size += size;
}
void vec_clone(void **dest, void **src)
{
if (!dest || !src || !*src)
return;
vec_make(dest, VEC_SIZE(*src));
memcpy(*dest, *src, VEC_SIZE(*src));
VEC_SIZE(*dest) = VEC_SIZE(*src);
}
#endif
#endif