Compare commits

...

15 Commits

Author SHA1 Message Date
Aryadev Chavali
3d738fc123 merge prick_functions and prick_macros into one lisp package 2026-03-26 11:19:14 +00:00
Aryadev Chavali
6353268c7b prick_sv: PRICK_SHORTHAND 2026-03-17 21:16:41 +00:00
Aryadev Chavali
bdd5b5c5a8 prick_darr: PRICK_SHORTHAND 2026-03-17 21:16:32 +00:00
Aryadev Chavali
3bebc86991 prick_btree: PRICK_SHORTHAND 2026-03-17 21:08:21 +00:00
Aryadev Chavali
192efb5aef prick_sv|prick_btree: some tasks 2026-03-17 21:05:38 +00:00
Aryadev Chavali
88cfe77b5f prick_arena: shorthand 2026-03-17 21:04:31 +00:00
Aryadev Chavali
65c424530f prick_arena: major bug fixes and namespacing 2026-03-17 20:59:48 +00:00
Aryadev Chavali
8a3ae735dc prick_vec: PRICK_SHORTHAND quality of life feature
A little QoL feature implemented as a preprocesser flag, where we
provide macros without the `prick_` prefix that link to the prick_vec
functions.
2026-03-17 20:36:48 +00:00
Aryadev Chavali
277606d483 prick_vec: added vec_pop and vec_find 2026-03-17 20:36:39 +00:00
Aryadev Chavali
ead8983ded deleted prick.org and split TODOs into header files
Makes more sense - any users of libraries should be aware of what
tasks are to be done, even if they don't have access to the repo
itself.
2026-03-17 20:28:28 +00:00
Aryadev Chavali
e370be62bf prick_sv: fix some mistakes 2026-03-09 07:16:38 +00:00
Aryadev Chavali
50e4509204 typing error in prick.functions 2026-03-07 23:49:00 +00:00
Aryadev Chavali
a6af885f41 Adjust in-package for prick.macros 2026-03-07 23:46:28 +00:00
Aryadev Chavali
0e05452f27 Adjust README for new common lisp libraries. 2026-03-07 23:43:05 +00:00
Aryadev Chavali
19f4d24e6d prick_functions and prick_macros: common lisp libraries 2026-03-07 23:42:50 +00:00
8 changed files with 442 additions and 103 deletions

View File

@@ -9,13 +9,32 @@
└──────────────────────────────────┘
#+end_example
A set of STB-style header-only libraries for common data structures
and algorithms that I may require while working on a C code base. The
idea is to be as close to "plug-and-play" as possible.
A set of libraries that attempt to reduce boilerplate for the
programming languages I use. The idea is for each library to be "plug
and play", which means complying with the following rules:
- A library is composed of a single file
- A library is only dependent on the core utilities provided by the
language i.e. they cannot be dependent on each other.
- A library should be structured to allow easy reading and extraction
of functionality if and when required.
All you need to do is copy the relevant header files over to your
project, then setup the implementation code within one code unit by:
These rules allow any library to be near trivial to plug into a
project - just copy over the file and utilise it. See below for more
details on the libraries provided for each language. Happy coding!
* C
The PRICK libraries for C are STB-style header-only libraries. All
you need to do is copy the relevant header file for a library over to
your project, then setup the implementation code within one (1) code
unit:
#+begin_src c
#define <LIB>_IMPL
#include "./<lib.h>"
#+end_src
See the commentary of the library for specific details regarding this.
* Common Lisp
The PRICK libraries for Common Lisp each have a ~defpackage~ at the
top of the file. You may move this to your ~packages.lisp~ if you
have one. The implementation is stored within the package, in the
same file.

184
prick.lisp Normal file
View File

@@ -0,0 +1,184 @@
;;; prick.lisp - 2026-03-26
;; Copyright (C) 2026 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/>.
;;; Commentary:
;; A set of useful functions, macros, and types that I've implemented enough
;; times to require their own prick library. There are a couple ways you can
;; use this file:
;; 1) Copy file and load it in your main.lisp. Ensure your code is in a
;; separate package for namespacing purposes.
;; 2) Copy file, move `defpackage' form into your packages.lisp, and add this
;; file as a component in your ASDF system definition.
;;; Code:
(defpackage #:prick
(:use :cl)
(:export
;; Threading macros
:--> :->> :-<>
;; Anonymous function constructors utilising threading macros
:$-> :$>> :$<>
;; Strictly typed functions and function calling
:-> :fn :call-rev
;; General purpose functions
:range :split :remove-at-indices :rev-map))
(in-package #:prick)
(defun --transform-symbols-to-unary (form)
(if (symbolp form)
(list form)
form))
(defmacro --> (placeholder &body forms)
"Fold `forms' recursively such that, given consecutive forms, the first form is
lexically bound to `placeholder' for the second form. Evaluate the form
generated after folding has completed.
(--> x (a1 a2...) (b1 b2...) (c1 c2...)) =>
(let ((x (a1 a2 ...)))
(let ((x (b1 b2 ...)))
(let ((x (c1 c2 ...)))
x)))
Includes transformer where symbols (after the first form) are considered
unary functions i.e.
(--> x a b c) =>
(let ((x a))
(let ((x (b x)))
(let ((x (c x)))
x)))"
`(let* ,(loop :for i :from 1
:for func :in (cdr forms)
:collect (list placeholder (--transform-symbols-to-unary func)) :into xs
:finally (return (cons `(,placeholder ,(car forms)) xs)))
,placeholder))
(defmacro ->> (&rest forms)
"Fold FORMS recursively such that, given consecutive forms, the first form
becomes the last argument of the second form. Evaluate the form generated after
folding has completed.
(->> (a1 ... al) (b1 ... bm) (c1 ... cm)) => (c1 ... cm (b1 ... bn (a1 ... al)))
Includes transformer where symbols (after the first form) are considered unary
functions i.e. (->> a b c) => (c (b a))"
(loop :with acc := (car forms)
:for func :in (cdr forms)
:do (setq acc (append (--transform-symbols-to-unary func) (list acc)))
:finally (return acc)))
(defmacro -<> (&rest forms)
"Fold FORMS recursively such that, given consecutive forms, the first form
becomes the first argument of the second form. Evaluate the form generated
after folding has completed.
(-<> (a1 ... al) (b1 ... bm) (c1 ... cn)) => (c1 (b1 (a1 ... al) ... bm) ... cn)
Includes transformer where symbols (after the first form) are considered unary
functions i.e. (-<> a b c) => (c (b a))"
(loop :with acc = (car forms)
:for func :in (cdr forms)
:for canon-func := (--transform-symbols-to-unary func)
:do (push acc (cdr canon-func))
:do (setq acc canon-func)
:finally (return acc)))
(defmacro $-> (capture &rest forms)
"Return an anonymous unary function (with argument named `capture') that feeds
its argument into a `-->' chain composed of `forms'. Note that `capture' is
also used as the placeholder value in said `-->' chain."
`(lambda (,capture)
(--> ,capture ,capture ,@forms)))
(defmacro $>> (&rest forms)
"Return an anonymous unary function that feeds its argument into a `->>' chain
composed of `forms'."
(let ((capture (gensym)))
`(lambda (,capture)
(->> ,capture ,@forms))))
(defmacro $<> (&rest forms)
"Return an anonymous unary function that feeds its argument into a `-<>' chain
composed of `forms'."
(let ((capture (gensym)))
`(lambda (,capture)
(-<> ,capture ,@forms))))
(deftype -> (args result)
"Simple type alias for functions."
`(function ,args ,result))
(defmacro fn (name lambda-list type &body body)
"Construct a function `name' that takes arguments `lambda-list' with body
`body'. `type' is used as the type of the function constructed via a declaim."
`(progn
(declaim (ftype ,type ,name))
(defun ,name ,lambda-list
,@body)))
(defmacro call-rev (func-name &rest arguments)
"Call function `func-name' with `arguments' reversed.
i.e. (call-rev f arg-1 ... arg-n) => (f arg-n ... arg-1).
Interacts well with the threading macro family (`-->', `->>', `-<>')"
`(,func-name ,@(reverse arguments)))
(fn range (&key (start 0) (end 0) (step 1))
(-> (&key (:start fixnum) (:end fixnum) (:step fixnum)) list)
"Return list of integers in interval [`start', `end'). If `step' is not 1,
then each member is `step' distance apart i.e. {`start' + (n * `step') | n from 0
till END}.
If END is not given, return interval [0, START)."
(declare (type integer start end step))
(if (< end start)
(error (format nil "~a < ~a" end start))
(loop :for i :from start :to (1- end) :by step
:collect i)))
(fn split (n lst) (-> (fixnum sequence) (values sequence sequence))
"Return two sequences of `lst': lst[0..`n'] and lst[`n'..]."
(values (subseq lst 0 n)
(subseq lst n)))
(fn remove-at-indices (indices lst) (-> (list sequence) list)
"Return `lst' with all items at an index specified in `indices' removed.
i.e. (remove-at-indices indices (l-1...l-m)) => (l-x where x is not in indices)."
(loop :for i :from 0 :to (1- (length lst))
:for item :in (coerce lst 'list)
:if (not (member i indices))
:collect item))
(fn rev-map (indicator lst &key (key-eq #'eq))
(-> (function list &key (:key-eq function)) list)
"Given some sequence of elements `lst' and a function `indicator': `lst' -> A for
some set A, return the reverse mapping of `indicator' on `lst'
i.e. Return `indicator'^-1: A -> {`lst'}.
`key-eq' is used for testing if any two elements of A are equivalent."
(declare (type (function (t) t) indicator)
(type sequence lst)
(type (function (t t) boolean) key-eq))
(loop :with assoc-list := nil
:for element :in (coerce lst 'list)
:for key := (funcall indicator element)
:if (assoc key assoc-list :test key-eq)
:do (push element (cdr (assoc key assoc-list :test key-eq)))
:else
:do (setq assoc-list (cons (list key element) assoc-list))
:finally (return assoc-list)))

View File

@@ -1,18 +0,0 @@
#+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

@@ -9,6 +9,11 @@
#include "prick_arena.h"
in one of your code units.
To remove the `prick_` namespacing, please put:
#define PRICK_SHORTHAND
in any files before including prick_arena.h. Standard preprocesser rules apply
with regards to hierarchy.
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.
@@ -25,28 +30,30 @@
#include <stdint.h>
typedef struct Region
// A region is a fixed sized allocation on the heap. Allocations against a
// region will depend on the difference between `size` and `capacity` and may
// fail if there isn't enough free space. Regions are arranged in a linked list
// to allow for recursion on failure to allocate.
typedef struct PrickArenaRegion
{
struct Region *next;
struct PrickArenaRegion *next;
uint32_t size, capacity;
uint8_t bytes[];
} region_t;
region_t *region_make(uint32_t capacity, region_t *next);
uint8_t *region_alloc_flat(region_t *region, uint32_t size);
uint8_t *region_alloc_rec(region_t *region, uint32_t size);
void region_delete_rec(region_t *region);
} prick_arena_region_t;
// An arena is simply a linked list of regions.
typedef struct
{
region_t *beg, *end;
} arena_t;
prick_arena_region_t *beg, *end;
} prick_arena_t;
uint8_t *arena_alloc(arena_t *arena, uint32_t size);
uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size,
uint32_t new_size);
void arena_reset(arena_t *arena);
void arena_free(arena_t *arena);
uint8_t *prick_arena_alloc(prick_arena_t *arena, uint32_t size);
uint8_t *prick_arena_realloc(prick_arena_t *arena, uint8_t *pointer,
uint32_t old_size, uint32_t new_size);
// Reset the arena but do not delete any regions, allowing for fresh allocations
// to take place.
void prick_arena_reset(prick_arena_t *arena);
void prick_arena_free(prick_arena_t *arena);
#ifdef PRICK_ARENA_IMPL
@@ -61,20 +68,26 @@ void arena_free(arena_t *arena);
#define REGION_CAPACITY_MULT 2
#endif
#ifndef MAX
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#endif
region_t *region_make(uint32_t capacity, region_t *next)
#ifndef MIN
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#endif
prick_arena_region_t *prick_region_make(uint32_t capacity,
prick_arena_region_t *next)
{
capacity = MAX(capacity, REGION_DEFAULT_SIZE);
region_t *region = calloc(1, sizeof(*region) + capacity);
prick_arena_region_t *region = calloc(1, sizeof(*region) + capacity);
region->next = next;
region->size = 0;
region->capacity = capacity;
return region;
}
uint8_t *region_alloc_rec(region_t *region, uint32_t capacity)
uint8_t *prick_region_alloc_rec(prick_arena_region_t *region, uint32_t capacity)
{
if (!region)
return NULL;
@@ -86,7 +99,8 @@ uint8_t *region_alloc_rec(region_t *region, uint32_t capacity)
if (region->capacity - region->size < capacity)
{
// no region->next, so make a new region that can fit the capacity required.
region_t *new = region_make(capacity * REGION_CAPACITY_MULT, NULL);
prick_arena_region_t *new =
prick_region_make(capacity * REGION_CAPACITY_MULT, NULL);
region->next = new;
region = new;
}
@@ -96,86 +110,102 @@ uint8_t *region_alloc_rec(region_t *region, uint32_t capacity)
return start;
}
void region_delete_rec(region_t *region)
void prick_region_delete_rec(prick_arena_region_t *region)
{
for (region_t *next = NULL; region;
for (prick_arena_region_t *next = NULL; region;
next = region->next, free(region), region = next)
continue;
}
uint8_t *arena_alloc(arena_t *arena, uint32_t size)
uint8_t *prick_arena_alloc(prick_arena_t *arena, uint32_t size)
{
if (!arena->beg)
{
arena->beg = region_make(size, NULL);
arena->beg = prick_region_make(size, NULL);
arena->end = arena->beg;
}
uint8_t *start = region_alloc_rec(arena->beg, size);
uint8_t *start = prick_region_alloc_rec(arena->beg, size);
// if we've attached a new region, end needs to be at that region
if (arena->end->next)
arena->end = arena->end->next;
return start;
}
uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size,
uint32_t new_size)
uint8_t *prick_arena_realloc(prick_arena_t *arena, uint8_t *ptr,
uint32_t old_size, uint32_t new_size)
{
if (!ptr)
{
if (!pointer)
// Basically the same as allocating at this point
return arena_alloc(arena, newsize);
// Firstly find the region the pointer exists in
region_t *prev, *reg;
for (prev = NULL, reg = arena->beg;
reg &&
!(reg->bytes <= pointer && reg->bytes + reg->size >= pointer + old_size);
prev = reg, reg = reg->next)
continue;
uint8_t *new_ptr = NULL;
// pointer isn't allocated in the arena, just allocate a new pointer
if (!reg)
goto arena_realloc__allocate_new;
return prick_arena_alloc(arena, new_size);
}
/*
If `ptr` is the latest allocation in `reg` and `reg` has enough capacity to
handle newsize, then we can adjust `reg` and not have to do any further
allocation work.
This check is not UB because ptr is confirmed to be in reg.
There are two options for reallocation strategy depending on where `ptr`
lies in its parent region (NOTE: certainly there can only be one parent
region):
1) If `ptr` lies at the end of the allocation and there is enough space to
fit the new_size requirement, just bump the parent region's size and
return the pointer (nearly "for free")
2) Otherwise, we must allocate a new block of memory and copy data over.
*/
if (ptr + oldsize == reg->bytes + reg->size &&
(reg->capacity - reg->size) > (newsize - oldsize))
// Linear search through our regions until we find parent region for `ptr`.
// FIXME: I'm pretty sure this is technically UB since we will almost
// certainly be comparing different batch allocations. Shame!
prick_arena_region_t *parent = NULL;
for (parent = arena->beg;
parent && !(parent->bytes <= ptr &&
parent->bytes + parent->size >= ptr + old_size);
parent = parent->next)
{
reg->size += newsize - oldsize;
continue;
}
// If `parent` is not NULL, then it is the region hosting `ptr`. Check if it
// satisfies the "lies at the end" condition.
if (parent && (ptr + old_size == parent->bytes + parent->size) &&
((parent->capacity - parent->size) > (new_size - old_size)))
{
parent->size += new_size - old_size;
return ptr;
}
arena_realloc__allocate_new:
new_ptr = arena_alloc(arena, newsize);
memcpy(new_ptr, ptr, oldsize);
// Otherwise, we'll need to reallocate.
uint8_t *new_ptr = prick_arena_alloc(arena, new_size);
memcpy(new_ptr, ptr, old_size);
return new_ptr;
}
void arena_reset(arena_t *arena)
void prick_arena_reset(prick_arena_t *arena)
{
for (region_t *region = arena->beg; region; region = region->next)
for (prick_arena_region_t *region = arena->beg; region; region = region->next)
{
region->size = 0;
memset(region->bytes, 0, region->capacity);
memset(region->bytes, 0, region->size);
}
}
void arena_free(arena_t *arena)
void prick_arena_free(prick_arena_t *arena)
{
region_delete_rec(arena->beg);
prick_region_delete_rec(arena->beg);
memset(arena, 0, sizeof(*arena));
}
#endif
#ifdef PRICK_SHORTHAND
typedef prick_arena_region_t arena_region_t;
typedef prick_arena_t arena_t;
#define arena_alloc prick_arena_alloc
#define arena_realloc prick_arena_realloc
#define arena_reset prick_arena_reset
#define arena_free prick_arena_free
#endif
#endif
/* Copyright (C) 2024 Aryadev Chavali

View File

@@ -9,8 +9,17 @@
#include "prick_btree.h"
in one of your code units.
To remove the `prick_` namespacing, please put:
#define PRICK_SHORTHAND
in any files before including prick_btree.h. Standard preprocesser rules apply
with regards to hierarchy.
An ordered binary tree implementation, allowing the use of custom comparators
and allocators.
Tasks:
- TODO: Pack user custom functions (allocate, comparison, etc) into a
structure.
*/
#ifndef PRICK_BTREE_H
@@ -143,6 +152,23 @@ void prick_btree_print(prick_bnode_t *root, FILE *fp,
#endif
#ifdef PRICK_SHORTHAND
typedef prick_bnode_t bnode_t;
typedef prick_btree_comp_fn btree_comp_fn;
typedef prick_btree_alloc_fn btree_alloc_fn;
typedef prick_btree_free_fn btree_free_fn;
typedef prick_btree_print_fn btree_print_fn;
#define btree_insert prick_btree_insert;
#define btree_right_rotate prick_btree_right_rotate;
#define btree_left_rotate prick_btree_left_rotate;
#define btree_print prick_btree_print;
#define btree_free prick_btree_free;
#endif
#endif
/* Copyright (C) 2025 Aryadev Chavali

View File

@@ -9,6 +9,11 @@
#include "prick_darr.h"
in one of your code units.
To remove the `prick_` namespacing, please put:
#define PRICK_SHORTHAND
in any files before including prick_darr.h. Standard preprocesser rules apply
with regards to hierarchy.
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
@@ -22,6 +27,9 @@
You may want to consider prick_vec.h if you want to more explicit control of
the dynamic array, and would like a stable pointer to the container itself.
Tasks:
- TODO: Implement ability to use a custom allocator.
*/
#ifndef PRICK_DARR_H
@@ -143,6 +151,21 @@ void prick_darr_clone(void **dest, void **src)
#endif
#ifdef PRICK_SHORTHAND
#endif
typedef prick_darr_t darr_t;
#define DARR_GET PRICK_DARR_GET
#define DARR_SIZE PRICK_DARR_SIZE
#define DARR_CAP PRICK_DARR_CAP
#define darr_make prick_darr_make
#define darr_free prick_darr_free
#define darr_append_byte prick_darr_append_byte
#define darr_append prick_darr_append
#define darr_clone prick_darr_clone
#define darr_ensure_remaining prick_darr_ensure_remaining
#endif
/* Copyright (C) 2024 Aryadev Chavali

View File

@@ -9,6 +9,11 @@
#include "prick_sv.h"
in one of your code units.
To remove the `prick_` namespacing, please put:
#define PRICK_SHORTHAND
in any files before including prick_sv.h. Standard preprocesser rules apply
with regards to hierarchy.
This is a simple read-only string view library. It defines some extremely
common functions you'd expect for a string view library, excluding any that
require allocation.
@@ -47,23 +52,23 @@ prick_sv_t prick_sv_while(prick_sv_t, const char *accept);
prick_sv_t prick_sv_chop_left(prick_sv_t sv, uint64_t size)
{
if (prick_sv.size <= size)
if (sv.size <= size)
return PRICK_SV(NULL, 0);
return PRICK_SV(prick_sv.data + size, prick_sv.size - size);
return PRICK_SV(sv.data + size, sv.size - size);
}
prick_sv_t prick_sv_chop_right(prick_sv_t sv, uint64_t size)
{
if (prick_sv.size <= size)
if (sv.size <= size)
return PRICK_SV(NULL, 0);
return PRICK_SV(prick_sv.data, prick_sv.size - size);
return PRICK_SV(sv.data, sv.size - size);
}
prick_sv_t prick_sv_truncate(prick_sv_t sv, uint64_t newsize)
{
if (newsize > prick_sv.size)
if (newsize > sv.size)
return PRICK_SV(NULL, 0);
return PRICK_SV(prick_sv.data, newsize);
return PRICK_SV(sv.data, newsize);
}
prick_sv_t prick_sv_substr(prick_sv_t sv, uint64_t position, uint64_t size)
@@ -74,12 +79,11 @@ prick_sv_t prick_sv_substr(prick_sv_t sv, uint64_t position, uint64_t size)
prick_sv_t prick_sv_till(prick_sv_t sv, const char *reject)
{
if (prick_sv.size == 0 || !prick_sv.data)
if (sv.size == 0 || !sv.data)
return PRICK_SV(NULL, 0);
uint64_t offset;
for (offset = 0;
offset < prick_sv.size && strchr(reject, prick_sv.data[offset]) == NULL;
for (offset = 0; offset < sv.size && strchr(reject, sv.data[offset]) == NULL;
++offset)
continue;
@@ -88,12 +92,11 @@ prick_sv_t prick_sv_till(prick_sv_t sv, const char *reject)
prick_sv_t prick_sv_while(prick_sv_t sv, const char *accept)
{
if (prick_sv.size == 0 || !prick_sv.data)
if (sv.size == 0 || !sv.data)
return PRICK_SV(NULL, 0);
uint64_t offset;
for (offset = 0;
offset < prick_sv.size && strchr(accept, prick_sv.data[offset]) != NULL;
for (offset = 0; offset < sv.size && strchr(accept, sv.data[offset]) != NULL;
++offset)
continue;
@@ -102,6 +105,22 @@ prick_sv_t prick_sv_while(prick_sv_t sv, const char *accept)
#endif
#ifdef PRICK_SHORTHAND
typedef prick_sv_t sv_t;
#define SV PRICK_SV
#define SV_AUTO PRICK_SV_AUTO
#define SV_FMT PRICK_SV_FMT
#define PR_SV PR_PRICK_SV
#define sv_chop_left prick_sv_chop_left
#define sv_chop_right prick_sv_chop_right
#define sv_truncate prick_sv_truncate
#define sv_substr prick_sv_substr
#define sv_till prick_sv_till
#define sv_while prick_sv_while
#endif
#endif
/* Copyright (C) 2026 Aryadev Chavali

View File

@@ -9,6 +9,11 @@
#include "prick_vec.h"
in one of your code units.
To remove the `prick_` namespacing, please put:
#define PRICK_SHORTHAND
in any files before including prick_vec.h. Standard preprocesser rules apply
with regards to hierarchy.
This library defines another form of dynamically sized array as opposed to
prick_darr.h. This one is closer to the one classically implemented by most; a
structure with some metadata and a pointer to the raw buffer. This way,
@@ -20,7 +25,10 @@
PRICK_VEC_INLINE_CAPACITY). This makes lookup _even faster_ (no derefence and
possibility of the entire vector existing in the CPU cache) and allows us to
avoid allocation for smaller use cases. If the number of elements exceeds
PRICK_VEC_INLINE_CAPACITY, we utilise the allocator.
PRICK_VEC_INLINE_CAPACITY, we utilise the generic memory allocator (malloc).
Tasks:
- TODO: Provide generic allocator support.
*/
#ifndef PRICK_VEC_H
@@ -49,11 +57,13 @@ static_assert(sizeof(prick_vec_t) == 64,
void prick_vec_append(prick_vec_t *vec, const void *const ptr, uint64_t size);
void prick_vec_append_byte(prick_vec_t *vec, uint8_t byte);
void *prick_vec_data(prick_vec_t *vec);
uint8_t *prick_vec_data(prick_vec_t *vec);
void prick_vec_ensure_capacity(prick_vec_t *vec, uint64_t capacity);
void prick_vec_ensure_free(prick_vec_t *vec, uint64_t size);
void prick_vec_free(prick_vec_t *vec);
void prick_vec_clone(prick_vec_t *v2, prick_vec_t *v1);
void *prick_vec_pop(prick_vec_t *vec, size_t member_size);
size_t prick_vec_find(prick_vec_t *vec, void *ptr, size_t ptrsize);
#define PRICK_VEC_GET(VEC, INDEX, TYPE) (((TYPE *)prick_vec_data(VEC))[INDEX])
@@ -81,7 +91,7 @@ void prick_vec_append_byte(prick_vec_t *vec, uint8_t byte)
++vec->size;
}
void *prick_vec_data(prick_vec_t *vec)
uint8_t *prick_vec_data(prick_vec_t *vec)
{
if (!vec)
return NULL;
@@ -146,9 +156,55 @@ void prick_vec_clone(prick_vec_t *v2, prick_vec_t *v1)
prick_vec_append(v2, prick_vec_data(v1), v1->size);
}
void *prick_vec_pop(prick_vec_t *vec, size_t member_size)
{
if (vec->size < member_size)
{
return NULL;
}
vec->size -= member_size;
return prick_vec_data(vec) + vec->size;
}
size_t prick_vec_find(prick_vec_t *vec, void *ptr, size_t ptrsize)
{
if (vec->size < ptrsize)
{
return vec->size + 1;
}
uint8_t *base = prick_vec_data(vec);
for (size_t i = 0; i < vec->size; i += ptrsize)
{
void *member = base + i;
if (!memcmp(member, ptr, ptrsize))
{
return i;
}
}
return vec->size + 1;
}
#undef MAX
#endif
#ifdef PRICK_SHORTHAND
typedef prick_vec_t vec_t;
#define vec_append prick_vec_append
#define vec_append_byte prick_vec_append_byte
#define vec_data prick_vec_data
#define vec_ensure_capacity prick_vec_ensure_capacity
#define vec_ensure_free prick_vec_ensure_free
#define vec_free prick_vec_free
#define vec_clone prick_vec_clone
#define vec_pop prick_vec_pop
#define vec_find prick_vec_find
#define VEC_GET PRICK_VEC_GET
#endif
#endif
/* Copyright (C) 2026 Aryadev Chavali