From 9781384d301fecb29882d361e3b2a30324aea93e Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Mon, 9 Mar 2026 07:19:47 +0000 Subject: [PATCH] added some prick libraries for useful data structures --- lib/prick_aliases.h | 44 ++++++++++++ lib/prick_sv.h | 115 +++++++++++++++++++++++++++++++ lib/prick_vec.h | 164 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 lib/prick_aliases.h create mode 100644 lib/prick_sv.h create mode 100644 lib/prick_vec.h diff --git a/lib/prick_aliases.h b/lib/prick_aliases.h new file mode 100644 index 0000000..a25440c --- /dev/null +++ b/lib/prick_aliases.h @@ -0,0 +1,44 @@ +/* prick_aliases.h: + * Created: 2025-04-09 + * 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 PRICK_ALIASES_H +#define PRICK_ALIASES_H + +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +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 . + + */ diff --git a/lib/prick_sv.h b/lib/prick_sv.h new file mode 100644 index 0000000..d360701 --- /dev/null +++ b/lib/prick_sv.h @@ -0,0 +1,115 @@ +/* prick_sv.h: String Views. + * Created: 2026-03-01 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + + To utilise this library, please put: + #define PRICK_SV_IMPL + #include "prick_sv.h" + in one of your code units. + + 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. + */ + +#ifndef PRICK_SV_H +#define PRICK_SV_H + +#include + +typedef struct +{ + uint64_t size; + const char *data; +} prick_sv_t; + +#define PRICK_SV(DATA, SIZE) ((prick_sv_t){.data = (DATA), .size = (SIZE)}) +#define PRICK_SV_AUTO(DATA) \ + ((prick_sv_t){.data = (void *)(DATA), .size = sizeof(DATA) - 1}) + +// Pretty printers +#define PRICK_SV_FMT(SV) (int)(SV).size, (SV).data +#define PR_PRICK_SV "%.*s" + +prick_sv_t prick_sv_chop_left(prick_sv_t, uint64_t size); +prick_sv_t prick_sv_chop_right(prick_sv_t, uint64_t size); +prick_sv_t prick_sv_truncate(prick_sv_t, uint64_t newsize); +prick_sv_t prick_sv_substr(prick_sv_t, uint64_t position, uint64_t size); + +prick_sv_t prick_sv_till(prick_sv_t, const char *reject); +prick_sv_t prick_sv_while(prick_sv_t, const char *accept); + +#ifdef PRICK_SV_IMPL +#include +#include + +prick_sv_t prick_sv_chop_left(prick_sv_t sv, uint64_t size) +{ + if (sv.size <= size) + return PRICK_SV(NULL, 0); + return PRICK_SV(sv.data + size, sv.size - size); +} + +prick_sv_t prick_sv_chop_right(prick_sv_t sv, uint64_t size) +{ + if (sv.size <= size) + return PRICK_SV(NULL, 0); + return PRICK_SV(sv.data, sv.size - size); +} + +prick_sv_t prick_sv_truncate(prick_sv_t sv, uint64_t newsize) +{ + if (newsize > sv.size) + return PRICK_SV(NULL, 0); + return PRICK_SV(sv.data, newsize); +} + +prick_sv_t prick_sv_substr(prick_sv_t sv, uint64_t position, uint64_t size) +{ + prick_sv_t result = prick_sv_truncate(prick_sv_chop_left(sv, position), size); + return result; +} + +prick_sv_t prick_sv_till(prick_sv_t sv, const char *reject) +{ + if (sv.size == 0 || !sv.data) + return PRICK_SV(NULL, 0); + + uint64_t offset; + for (offset = 0; offset < sv.size && strchr(reject, sv.data[offset]) == NULL; + ++offset) + continue; + + return prick_sv_truncate(sv, offset); +} + +prick_sv_t prick_sv_while(prick_sv_t sv, const char *accept) +{ + if (sv.size == 0 || !sv.data) + return PRICK_SV(NULL, 0); + + uint64_t offset; + for (offset = 0; offset < sv.size && strchr(accept, sv.data[offset]) != NULL; + ++offset) + continue; + + return prick_sv_truncate(sv, offset); +} + +#endif + +#endif + +/* 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 . + + */ diff --git a/lib/prick_vec.h b/lib/prick_vec.h new file mode 100644 index 0000000..0b88494 --- /dev/null +++ b/lib/prick_vec.h @@ -0,0 +1,164 @@ +/* prick_vec.h: A dynamically sized array with SBO. + * Created: 2026-01-22 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + + To utilise this library, please put: + #define PRICK_VEC_IMPL + #include "prick_vec.h" + in one of your code units. + + 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, + pointers to the dynamic array are stable (as the structure itself is never + reallocated) and the array can still grow as required. + + We use a trick, called Small Buffer Optimisation (SBO), to inline elements + directly into the structure when there are a small number of them (see + 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. + */ + +#ifndef PRICK_VEC_H +#define PRICK_VEC_H + +#include +#include +#include + +#define PRICK_VEC_INLINE_CAPACITY 32 +#define PRICK_VEC_MULT 2 + +typedef struct +{ + uint64_t size, capacity; + uint8_t not_inlined; + union + { + void *ptr; + alignas(max_align_t) uint8_t inlined[PRICK_VEC_INLINE_CAPACITY]; + }; +} prick_vec_t; + +static_assert(sizeof(prick_vec_t) == 64, + "Expected sizeof(prick_vec_t) to be 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); +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); + +#define PRICK_VEC_GET(VEC, INDEX, TYPE) (((TYPE *)prick_vec_data(VEC))[INDEX]) + +#ifdef PRICK_VEC_IMPL +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +#include +#include + +void prick_vec_append(prick_vec_t *vec, const void *const ptr, uint64_t size) +{ + if (!vec || !ptr || !size) + return; + prick_vec_ensure_free(vec, size); + memcpy(&PRICK_VEC_GET(vec, vec->size, uint8_t), ptr, size); + vec->size += size; +} + +void prick_vec_append_byte(prick_vec_t *vec, uint8_t byte) +{ + if (!vec) + return; + prick_vec_ensure_free(vec, 1); + PRICK_VEC_GET(vec, vec->size, uint8_t) = byte; + ++vec->size; +} + +void *prick_vec_data(prick_vec_t *vec) +{ + if (!vec) + return NULL; + + if (vec->not_inlined) + { + return vec->ptr; + } + else + { + return vec->inlined; + } +} + +void prick_vec_ensure_capacity(prick_vec_t *vec, uint64_t capacity) +{ + if (!vec) + return; + if (vec->capacity == 0) + vec->capacity = PRICK_VEC_INLINE_CAPACITY; + if (vec->capacity < capacity) + { + vec->capacity = MAX(vec->capacity * PRICK_VEC_MULT, capacity); + if (!vec->not_inlined) + { + // We were a small buffer, and now we cannot be i.e. we need to allocate + // on the heap. + vec->not_inlined = 1; + void *buffer = calloc(1, vec->capacity); + memcpy(buffer, vec->inlined, vec->size); + memset(vec->inlined, 0, sizeof(vec->inlined)); + vec->ptr = buffer; + } + else + { + // We're already on the heap, just reallocate. + vec->ptr = realloc(vec->ptr, vec->capacity); + } + } +} + +void prick_vec_ensure_free(prick_vec_t *vec, uint64_t size) +{ + if (!vec) + return; + prick_vec_ensure_capacity(vec, vec->size + size); +} + +void prick_vec_free(prick_vec_t *vec) +{ + if (!vec) + return; + if (vec->not_inlined) + free(vec->ptr); + memset(vec, 1, sizeof(*vec)); +} + +void prick_vec_clone(prick_vec_t *v2, prick_vec_t *v1) +{ + if (!v1 || !v2) + return; + prick_vec_append(v2, prick_vec_data(v1), v1->size); +} + +#undef MAX +#endif + +#endif + +/* 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 . + + */