Files
prick/prick_vec.h

165 lines
4.3 KiB
C

/* 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 <assert.h>
#include <stddef.h>
#include <stdint.h>
#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 <stdlib.h>
#include <string.h>
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 <https://unlicense.org/>.
*/