uint8_t Overview
The uint8_t family of data structures in csalt provides type-safe,
allocator-agnostic containers for 8-bit unsigned integer data. Every
structure wraps the generic tensor_t engine from c_tensor.h with
the element type fixed to UINT8_TYPE at initialisation, eliminating
the need for the caller to pass a dtype_id_t argument to any operation.
All memory is managed through an allocator_vtable_t supplied by
the caller at initialisation time. csalt does not assume a default
allocator — the caller must explicitly provide one at every call site that
allocates memory (initialisation, copy, and slice). The library ships
several ready-made implementations and the interface is open so that custom
allocators can be substituted without changing any call sites. See
C Allocator Overview for the full allocator API, the list of available
implementations, and guidance on writing your own.
Error handling follows the expected value pattern throughout: operations
that produce a new object return a uint8_tensor_expect_t whose
has_value field distinguishes success from failure, while in-place
mutations return an error_code_t directly. Callers must always
check the result before using the value.
uint8_tensor_expect_t r = init_uint8_array(8, true, heap_allocator());
if (!r.has_value) {
fprintf(stderr, "%s\n", error_to_string(r.u.error));
}
uint8_tensor_t* arr = r.u.value;
/* ... use arr ... */
return_uint8_tensor(arr);
Note
Functions documented under uint8_t Tensor operate on any
uint8_tensor_t regardless of how it was initialised. Functions
documented under uint8_t Array are only valid for tensors
initialised via init_uint8_array — they require
mode == ARRAY_STRUCT and will return PRECONDITION_FAIL if
called on a tensor created by init_uint8_tensor.
uint8_t Tensor
The following types and functions operate on any uint8_tensor_t
regardless of dimensionality or mode. This includes fixed-shape N-D tensors
created with init_uint8_tensor and dynamic 1-D arrays created with
init_uint8_array.
Structs
-
struct uint8_tensor_t
Type-safe wrapper for a tensor containing uint8_t elements.
Holds a pointer to the underlying generic tensor rather than embedding it by value, which is required because tensor_t contains a flexible array member and cannot legally appear as a struct member in C99/C11. All generic tensor operations are reached through the base pointer. Use the typed wrapper functions (init_uint8_array, push_back_uint8_tensor, etc.) rather than accessing base directly.
-
struct uint8_tensor_expect_t
Expected-value return type for functions that produce a uint8_tensor_t.
has_value must be checked before accessing either union member.
uint8_tensor_expect_t r = init_uint8_array(8, true, heap_allocator()); if (!r.has_value) { handle(r.u.error); } uint8_tensor_t* a = r.u.value;
Initialisation and Teardown
-
uint8_tensor_expect_t init_uint8_tensor(uint8_t ndim, const size_t *shape, allocator_vtable_t alloc_v)
Initialise a fixed-shape N-dimensional tensor of uint8_t elements.
Allocates the underlying tensor_t via init_tensor and then allocates a thin uint8_tensor_t wrapper struct through the same allocator. All slots are zero-initialised at construction and considered live for the lifetime of the tensor — len equals alloc equals the product of all shape dimensions. Both allocations must be returned by calling return_uint8_tensor when the tensor is no longer needed.
// Create a 3x4 matrix of uint8_t values size_t shape[] = { 3u, 4u }; uint8_tensor_expect_t r = init_uint8_tensor(2u, shape, heap_allocator()); if (!r.has_value) { fprintf(stderr, "%s\n", error_to_string(r.u.error)); } uint8_tensor_t* mat = r.u.value; // Write to element (1, 2) size_t idx[] = { 1u, 2u }; set_uint8_tensor_nd_index(mat, idx, 42u); // Read it back uint8_t val; get_uint8_tensor_nd_index(mat, idx, &val); // val == 42 return_uint8_tensor(mat);
- Parameters:
ndim – Number of dimensions. Must be > 0 and <= 255.
shape – Array of ndim dimension sizes. Must not be NULL. Each shape[i] must be > 0.
alloc_v – Allocator vtable for all memory operations. alloc_v.allocate must not be NULL.
- Returns:
uint8_tensor_expect_t with has_value true on success. On failure has_value is false and u.error is set.
-
void return_uint8_tensor(uint8_tensor_t *t)
Return a uint8_tensor_t and its underlying tensor to the allocator.
Returns the data buffer and tensor_t struct via return_tensor, then returns the wrapper struct itself. The allocator is read from the tensor before return_tensor is called so it remains valid for the wrapper deallocation. Silently ignored if t is NULL.
uint8_tensor_expect_t r = init_uint8_array(8, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; // ... use arr ... return_uint8_tensor(arr); arr = NULL; // guard against accidental reuse
- Parameters:
t – Pointer to the wrapper to return. May be NULL.
-
uint8_tensor_expect_t copy_uint8_tensor(const uint8_tensor_t *src, allocator_vtable_t *alloc_v)
Create a deep copy of a uint8_tensor_t.
Copies the underlying tensor via copy_tensor then allocates a new uint8_tensor_t wrapper struct. Both allocations are returned by calling return_uint8_tensor on the returned pointer. On partial failure the copied tensor_t is returned before propagating the error so no memory is leaked. The copy is independent of src — mutations to one do not affect the other.
// Copy using src's own allocator uint8_tensor_expect_t c = copy_uint8_tensor(arr, NULL); if (!c.has_value) { fprintf(stderr, "%s\n", error_to_string(c.u.error)); } uint8_tensor_t* copy = c.u.value; // ... return_uint8_tensor(copy); // Copy using a different allocator allocator_vtable_t arena = arena_allocator(&my_arena); uint8_tensor_expect_t c2 = copy_uint8_tensor(arr, &arena);
- Parameters:
src – Pointer to the source uint8_tensor_t. Must not be NULL.
alloc_v – Pointer to the allocator vtable for the new memory. Pass NULL to inherit the allocator from src.
- Returns:
uint8_tensor_expect_t with has_value true on success. On failure has_value is false and u.error is set.
Get and Set
-
static inline error_code_t get_uint8_tensor_index(const uint8_tensor_t *t, size_t index, uint8_t *out)
Copy one element out of the array by flat index.
For ARRAY_STRUCT mode the index must be < len. For TENSOR_STRUCT mode the index must be < alloc. Returns NULL_POINTER if t or out is NULL, OUT_OF_BOUNDS if the index is out of range.
// arr = [10, 20, 30] uint8_t val; get_uint8_tensor_index(arr, 1u, &val); // val == 20
-
static inline error_code_t get_uint8_tensor_nd_index(const uint8_tensor_t *t, const size_t *idx, uint8_t *out)
Copy one element out of the tensor by N-dimensional index.
Only valid for TENSOR_STRUCT mode. Returns ILLEGAL_STATE if called on an ARRAY_STRUCT tensor. idx must point to an array of ndim indices. Returns NULL_POINTER if t, idx, or out is NULL, OUT_OF_BOUNDS if any index exceeds its dimension bound.
// For a 3x3 uint8 matrix size_t idx[] = { 1u, 2u }; uint8_t val; get_uint8_tensor_nd_index(mat, idx, &val); // val = mat[1][2]
-
static inline error_code_t set_uint8_tensor_index(uint8_tensor_t *t, size_t index, uint8_t data)
Overwrite the element at a flat index without changing len.
For ARRAY_STRUCT mode the index must be < len. For TENSOR_STRUCT mode the index must be < alloc. Returns NULL_POINTER if t is NULL, OUT_OF_BOUNDS if the index is out of range.
push_back_uint8_tensor(arr, 0u); // arr = [0] set_uint8_tensor_index(arr, 0u, 99u); // arr = [99]
-
static inline error_code_t set_uint8_tensor_nd_index(uint8_tensor_t *t, const size_t *idx, uint8_t data)
Overwrite the element at an N-dimensional index without changing len.
Only valid for TENSOR_STRUCT mode. Returns ILLEGAL_STATE if called on an ARRAY_STRUCT tensor. idx must point to an array of ndim indices. Returns NULL_POINTER if t or idx is NULL, OUT_OF_BOUNDS if any index exceeds its dimension bound.
// For a 3x3 uint8 matrix initialised via init_tensor size_t idx[] = { 1u, 2u }; set_uint8_tensor_nd_index(mat, idx, 42u); // mat[1][2] = 42
Utility Operations
-
static inline error_code_t clear_uint8_tensor(uint8_tensor_t *t)
Reset the array to empty without releasing its allocated buffer.
Zeroes all bytes in the data buffer and sets len to 0 if the tensor is an array. The allocated capacity, shape, strides, and mode are preserved. Returns NULL_POINTER if t is NULL.
push_back_uint8_tensor(arr, 1u); push_back_uint8_tensor(arr, 2u); clear_uint8_tensor(arr); size_t n = uint8_tensor_size(arr); // 0
-
uint8_tensor_expect_t slice_uint8_tensor_array(const uint8_tensor_t *src, size_t start, size_t end, allocator_vtable_t *alloc_v)
Return a new array containing a copy of the elements in src at positions [start, end).
The range is half-open: start is inclusive, end is exclusive. Both must be <= src->len and start must be strictly less than end. The returned array is independent of src — mutations to one do not affect the other. The caller is responsible for calling return_uint8_tensor on the result.
Pass NULL for alloc_v to inherit the allocator from src.
// arr = [10, 20, 30, 40, 50] uint8_tensor_expect_t s = slice_uint8_tensor_array(arr, 1u, 4u, NULL); if (!s.has_value) { fprintf(stderr, "%s\n", error_to_string(s.u.error)); } uint8_tensor_t* sl = s.u.value; // sl = [20, 30, 40] return_uint8_tensor(sl);
- Parameters:
src – Pointer to the source array. Must not be NULL.
start – Zero-based index of the first element to include.
end – Zero-based index one past the last element to include.
alloc_v – Pointer to the allocator vtable. NULL inherits from src.
- Returns:
uint8_tensor_expect_t with has_value true on success.
-
static inline error_code_t reverse_uint8_tensor(uint8_tensor_t *t)
Reverse the populated elements of a uint8_t tensor in place.
Reverses the order of all elements in the live region of the tensor’s data buffer using a SIMD-accelerated byte-swap routine. The operation is mode-agnostic — for ARRAY_STRUCT tensors it reverses the len populated elements, and for TENSOR_STRUCT tensors it reverses all alloc elements in flat row-major order.
The tensor is modified in place. No allocation is performed and the allocator is not consulted.
Returns EMPTY rather than NO_ERROR when len < 2 because a zero or single-element sequence has no meaningful reversal. The tensor is left untouched in this case.
// arr = [1, 2, 3, 4, 5] error_code_t err = reverse_uint8_tensor(arr); // arr = [5, 4, 3, 2, 1]
- Parameters:
t – Pointer to the target tensor. Must not be NULL.
- Returns:
NO_ERROR on success, or one of:
NULL_POINTER if t is NULL
EMPTY if t->base->len < 2
-
static inline error_code_t sort_uint8_tensor(uint8_tensor_t *t, direction_t dir)
Sort the populated elements of a uint8_t tensor in place.
Sorts the len elements in the tensor’s data buffer using an iterative quicksort with median-of-three pivot selection and an insertion sort fallback for partitions of fewer than 10 elements. The uint8_t comparator is supplied internally so the caller only needs to specify the sort direction.
The sort operates on the flat element sequence in memory — for an ARRAY_STRUCT tensor this covers only the len populated elements, and for a TENSOR_STRUCT tensor this covers all alloc elements in flat row-major order.
// arr = [3, 1, 4, 1, 5, 9, 2, 6] error_code_t err = sort_uint8_tensor(arr, FORWARD); // arr = [1, 1, 2, 3, 4, 5, 6, 9] err = sort_uint8_tensor(arr, REVERSE); // arr = [9, 6, 5, 4, 3, 2, 1, 1]
- Parameters:
t – Pointer to the target tensor. Must not be NULL.
dir – FORWARD for ascending order, REVERSE for descending order.
- Returns:
NO_ERROR on success, or one of:
NULL_POINTER if t is NULL
EMPTY if t->base->len < 2
-
error_code_t uint8_tensor_lsearch(const uint8_tensor_t *t, size_t *index, uint8_t value)
Search a uint8_t tensor for the first occurrence of a value.
Performs a linear scan of the populated elements in the tensor’s data buffer from index 0 to len - 1. On the first match, writes the zero-based index of the matching element into *index and returns NO_ERROR. If no match is found the function returns NOT_FOUND and *index is left unchanged.
The search covers only the populated region [0, len) — for ARRAY_STRUCT tensors this is the number of elements pushed so far, and for TENSOR_STRUCT tensors this is alloc (all slots are always live). When duplicates are present the index of the first occurrence is returned.
uint8_tensor_expect_t r = init_uint8_array(8, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; push_back_uint8_array(arr, 10u); push_back_uint8_array(arr, 20u); push_back_uint8_array(arr, 30u); push_back_uint8_array(arr, 20u); // arr = [10, 20, 30, 20] size_t idx = 0u; error_code_t err = find_uint8_tensor_value(arr, &idx, 20u); // err == NO_ERROR, idx == 1 (first occurrence) err = find_uint8_tensor_value(arr, &idx, 99u); // err == NOT_FOUND, idx unchanged return_uint8_tensor(arr);
- Parameters:
t – Pointer to the source tensor. Must not be NULL.
index – Pointer to a size_t that receives the index of the first matching element on success. Must not be NULL. Unchanged if the value is not found.
value – The uint8_t value to search for.
- Returns:
NO_ERROR on success, or one of:
NULL_POINTER if t or index is NULL
EMPTY if t->base->len == 0
NOT_FOUND if value is not present in the tensor
-
error_code_t uint8_tensor_bsearch(const uint8_tensor_t *t, size_t *index, uint8_t value)
Binary search a sorted uint8_t tensor for a target value.
Performs an iterative binary search over the populated elements of the tensor’s data buffer. The tensor must be sorted in ascending order before calling this function — the result is undefined if the data is unsorted. Use sort_uint8_tensor before calling if the sort order is not guaranteed.
When duplicates are present binary search does not guarantee which occurrence is returned — only that the returned index satisfies data[*index] == value. Use uint8_tensor_lsearch if the first occurrence is required.
An underflow guard prevents size_t wraparound when the target is smaller than every element: if the midpoint reaches index 0 and the element there does not match, the loop exits cleanly rather than decrementing high below zero.
uint8_tensor_expect_t r = init_uint8_array(8, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; push_back_uint8_array(arr, 10u); push_back_uint8_array(arr, 40u); push_back_uint8_array(arr, 20u); push_back_uint8_array(arr, 30u); // Sort before searching sort_uint8_tensor(arr, FORWARD); // arr = [10, 20, 30, 40] size_t idx = 0u; error_code_t err = uint8_tensor_bsearch(arr, &idx, 30u); // err == NO_ERROR, idx == 2 err = uint8_tensor_bsearch(arr, &idx, 25u); // err == NOT_FOUND, idx unchanged return_uint8_tensor(arr);
- Parameters:
t – Pointer to the source tensor. Must not be NULL. The tensor must be sorted in ascending order.
index – Pointer to a size_t that receives the zero-based index of the matching element on success. Must not be NULL. Unchanged if the value is not found.
value – The uint8_t value to search for.
- Returns:
NO_ERROR on success, or one of:
NULL_POINTER if t or index is NULL
EMPTY if t->base->len == 0
NOT_FOUND if value is not present in the tensor
-
bracket_expect_t uint8_tensor_bbsearch(const uint8_tensor_t *t, uint8_t value)
Bracketed binary search of a sorted uint8_t tensor.
Locates the pair of adjacent indices that bracket value in a sorted ascending tensor. Unlike uint8_tensor_bsearch which returns NOT_FOUND when an exact match is absent, this function always returns the nearest enclosing indices, making it suitable for interpolation, range queries, and nearest-neighbour lookups where the value is unlikely to exist exactly in the array.
The tensor must be sorted in ascending order before calling this function — the result is undefined if the data is unsorted.
Return cases:
Exact match: has_value == true, lower == upper == index of matching element.
Value bracketed: has_value == true, lower is the index of the largest element <= value, upper is the index of the smallest element >= value.
Value below all elements (value < data[0]): has_value == false, error == BELOW_RANGE, u.value.lower == 0, u.value.upper == 0. The caller can read the lower bound via u.value.upper.
Value above all elements (value > data[len-1]): has_value == false, error == ABOVE_RANGE, u.value.lower == len-1, u.value.upper == len-1. The caller can read the upper bound via u.value.lower.
// arr = [10, 20, 30, 40, 50] bracket_expect_t r; // Exact match r = uint8_tensor_bbsearch(arr, 30u); // r.has_value == true, r.u.value.lower == 2, r.u.value.upper == 2 // Bracketed r = uint8_tensor_bbsearch(arr, 25u); // r.has_value == true, r.u.value.lower == 1, r.u.value.upper == 2 // data[1]==20 <= 25 <= data[2]==30 // Below range r = uint8_tensor_bbsearch(arr, 5u); // r.has_value == false, r.u.error == BELOW_RANGE // r.u.value.upper == 0 (nearest element is data[0]==10) // Above range r = uint8_tensor_bbsearch(arr, 99u); // r.has_value == false, r.u.error == ABOVE_RANGE // r.u.value.lower == 4 (nearest element is data[4]==50)
- Parameters:
t – Pointer to the source tensor. Must not be NULL. Must be sorted in ascending order.
value – The uint8_t value to bracket.
- Returns:
bracket_expect_t:
has_value true → exact match or bracket found, u.value holds lower and upper indices.
has_value false → u.error is one of: NULL_POINTER if t is NULL EMPTY if t->base->len == 0 BELOW_RANGE if value < data[0]; u.value.upper == 0 ABOVE_RANGE if value > data[len-1]; u.value.lower == len-1
-
bool uint8_tensors_equal(const uint8_tensor_t *one, const uint8_tensor_t *two, bool meta)
Compare two uint8_t tensors for equality.
Compares the populated data buffers of two uint8_t tensors using memcmp. The comparison is always performed on the live region [0, len) — for ARRAY_STRUCT tensors this is the number of populated elements and for TENSOR_STRUCT tensors this is alloc (all slots are always live).
The meta flag controls whether structural metadata is included in the comparison:
meta == false: Only ndim and the data buffer contents are compared. Two tensors are considered equal if they have the same number of dimensions and identical byte content in the populated region, regardless of capacity, shape, mode, or growth policy. This is the appropriate comparison for value equality — “do these two tensors contain the
same data?”
meta == true: In addition to the checks above, shape, alloc, mode, and growth must all match. This is the appropriate comparison for structural equality — “are these two tensors the same object in every respect
except their allocator and memory address?”
The allocator vtable is never compared. Two tensors can be structurally identical and logically equal even if they were allocated by different allocators.
uint8_tensor_t* a = init_uint8_array(8, false, heap_allocator()).u.value; uint8_tensor_t* b = init_uint8_array(4, false, heap_allocator()).u.value; push_back_uint8_array(a, 1u); push_back_uint8_array(a, 2u); push_back_uint8_array(b, 1u); push_back_uint8_array(b, 2u); // Value equality — content matches, capacity difference ignored bool eq = uint8_tensors_equal(a, b, false); // true // Structural equality — capacity differs (8 vs 4) eq = uint8_tensors_equal(a, b, true); // false return_uint8_tensor(a); return_uint8_tensor(b);
- Parameters:
one – Pointer to the first tensor. Must not be NULL.
two – Pointer to the second tensor. Must not be NULL.
meta – If true, include shape, alloc, mode, and growth in the comparison in addition to ndim and data contents. If false, compare only ndim and data contents.
- Returns:
true if the tensors are equal under the selected comparison, or if one and two are the same pointer.
- Returns:
false if either pointer is NULL, either base data pointer is NULL, or any compared field differs.
-
error_code_t min_uint8_tensor(const uint8_tensor_t *t, uint8_t *value)
Find the minimum element in a uint8 tensor.
Scans all len populated elements and writes the smallest value into the caller-provided output. Uses a SIMD fast path when available (SSE2/SSSE3/SSE4.1/AVX/AVX2/AVX-512BW on x86-64, NEON/SVE/SVE2 on AArch64) and falls back to scalar iteration otherwise. The search short-circuits as soon as the global minimum for the type (0) is encountered.
Works on both TENSOR_STRUCT and ARRAY_STRUCT modes — the scan always covers exactly t->base->len elements.
uint8_t min_val; error_code_t err = min_uint8_tensor(arr, &min_val); if (err != NO_ERROR) { fprintf(stderr, "%s\n", error_to_string(err)); } printf("Minimum: %" PRIu8 "\n", min_val);
- Parameters:
t – Pointer to the source uint8 tensor. Must not be NULL.
value – Pointer to a uint8_t to receive the minimum value. Must not be NULL.
- Returns:
NO_ERROR on success, or one of:
NULL_POINTER if t, t->base, or value is NULL
EMPTY if t->base->data is NULL or t->base->len == 0
Introspection
-
static inline size_t uint8_tensor_size(const uint8_tensor_t *t)
Return the number of populated elements in the array.
For ARRAY_STRUCT tensors this is the number of elements pushed so far. For TENSOR_STRUCT tensors this equals alloc (all slots are always live). Returns 0 if t is NULL.
size_t n = uint8_tensor_size(arr); // e.g. 3 after three push_back calls
-
static inline size_t uint8_tensor_alloc(const uint8_tensor_t *t)
Return the allocated capacity of the array in elements.
Returns 0 if t is NULL.
size_t cap = uint8_tensor_alloc(arr); // e.g. 16 after init_uint8_array(16,...)
-
static inline size_t uint8_tensor_data_size(const uint8_tensor_t *t)
Return the size of one element in bytes.
Always returns sizeof(uint8_t) == 1 for a correctly initialised uint8_tensor_t. Returns 0 if t is NULL.
size_t esz = uint8_tensor_data_size(arr); // 1
-
static inline dtype_id_t uint8_tensor_dtype(const uint8_tensor_t *t)
Return the dtype identifier of the array.
Always returns UINT8_TYPE for a correctly initialised uint8_tensor_t. Returns UNKNOWN_TYPE if t is NULL.
dtype_id_t id = uint8_tensor_dtype(arr); // UINT8_TYPE
-
static inline uint8_t uint8_tensor_ndim(const uint8_tensor_t *t)
Return the number of dimensions of the underlying tensor.
Always returns 1 for a tensor initialised via init_uint8_array. Returns 0 if t is NULL.
uint8_t ndim = uint8_tensor_ndim(arr); // 1
-
static inline size_t uint8_tensor_shape_dim(const uint8_tensor_t *t, uint8_t dim)
Return the size of a single dimension.
For a 1-D array, dim must be 0. Returns 0 if t is NULL or dim is out of range.
uint8_tensor_expect_t r = init_uint8_array(8, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; size_t d0 = uint8_tensor_shape_dim(arr, 0u); // 8 return_uint8_tensor(arr);
-
static inline error_code_t uint8_tensor_shape(const uint8_tensor_t *t, size_t *out, uint8_t count)
Copy the shape of the tensor into a caller-provided buffer.
Copies min(ndim, count) dimension sizes into out. Returns INVALID_ARG if count < ndim (output truncated). Returns NULL_POINTER if t or out is NULL.
size_t shape[1]; error_code_t err = uint8_tensor_shape(arr, shape, 1u); // shape[0] now holds the single dimension size
-
static inline const size_t *uint8_tensor_shape_ptr(const uint8_tensor_t *t)
Return a read-only pointer to the internal shape array.
The pointer is valid for the lifetime of the tensor and must not be modified or freed. Returns NULL if t is NULL.
const size_t* sp = uint8_tensor_shape_ptr(arr); if (sp) printf("dim0 = %zu\n", sp[0]);
-
static inline const size_t *uint8_tensor_strides_ptr(const uint8_tensor_t *t)
Return a read-only pointer to the internal strides array.
Strides are in bytes. For a contiguous 1-D uint8_t array, strides[0] == 1. The pointer is valid for the lifetime of the tensor and must not be modified or freed. Returns NULL if t is NULL.
const size_t* strides = uint8_tensor_strides_ptr(arr); if (strides) printf("stride0 = %zu\n", strides[0]); // 1
-
static inline error_code_t uint8_tensor_shape_str(const uint8_tensor_t *t, char *buf, size_t buf_len)
Write a human-readable shape string into a caller-provided buffer.
Formats the shape as “(d0)” for a 1-D array. Intended for debugging and logging only. Returns CAPACITY_OVERFLOW if the buffer is too small. Returns NULL_POINTER if t or buf is NULL.
char buf[32]; error_code_t err = uint8_tensor_shape_str(arr, buf, sizeof(buf)); if (err == NO_ERROR) printf("shape = %s\n", buf); // e.g. "(8)"
-
static inline bool is_uint8_tensor_empty(const uint8_tensor_t *t)
Return true if the array contains no populated elements.
Returns true if t is NULL.
uint8_tensor_expect_t r = init_uint8_array(8, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; bool empty = is_uint8_tensor_empty(arr); // true before any push push_back_uint8_tensor(arr, 1u); empty = is_uint8_tensor_empty(arr); // false after push return_uint8_tensor(arr);
-
static inline bool is_uint8_tensor_full(const uint8_tensor_t *t)
Return true if the array is at full capacity.
Returns true if t is NULL.
uint8_tensor_expect_t r = init_uint8_array(2, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; push_back_uint8_tensor(arr, 1u); push_back_uint8_tensor(arr, 2u); bool full = is_uint8_tensor_full(arr); // true — len == alloc == 2 return_uint8_tensor(arr);
-
static inline bool is_uint8_tensor_ptr(const uint8_tensor_t *t, const uint8_t *ptr)
Return true if ptr points to a valid element-aligned address within the live region of the array’s data buffer.
The pointer must be >= data, < data + len * data_size, and aligned to an element boundary. Returns false if either argument is NULL.
uint8_tensor_expect_t r = init_uint8_array(4, false, heap_allocator()); uint8_tensor_t* arr = r.u.value; push_back_uint8_tensor(arr, 10u); const uint8_t* p = arr->base->data; bool valid = is_uint8_tensor_ptr(arr, p); // true — points to element 0 return_uint8_tensor(arr);
uint8_t Array
A uint8_tensor_t initialised via init_uint8_array operates in
ARRAY_STRUCT mode, providing a dynamic 1-D ordered sequence of
uint8_t values backed by a contiguous allocator-managed buffer. In this
mode len tracks the number of populated elements and alloc tracks
the total allocated capacity. The functions in this section are only valid
for tensors in ARRAY_STRUCT mode — calling them on a tensor created by
init_uint8_tensor returns PRECONDITION_FAIL.
#include "c_uint8.h"
/* Choose an allocator — see :ref:`allocator_file` for all options. */
allocator_vtable_t alloc = heap_allocator();
uint8_tensor_expect_t r = init_uint8_array(8, true, alloc);
if (!r.has_value) { /* handle r.u.error */ }
uint8_tensor_t* arr = r.u.value;
push_back_uint8_tensor(arr, 30u);
push_back_uint8_tensor(arr, 10u);
push_back_uint8_tensor(arr, 20u);
/* arr = [30, 10, 20], len == 3 */
uint8_t val;
pop_front_uint8_tensor(arr, &val); /* val == 30, arr = [10, 20] */
return_uint8_tensor(arr);
Initialisation and Teardown
-
uint8_tensor_expect_t init_uint8_array(size_t capacity, bool growth, allocator_vtable_t alloc_v)
Initialise a dynamic 1-D array of uint8_t elements.
Allocates the underlying tensor_t via init_tensor_array and then allocates a thin uint8_tensor_t wrapper struct through the same allocator. Both allocations must be returned by calling return_uint8_tensor when the array is no longer needed.
uint8_tensor_expect_t r = init_uint8_array(16, true, heap_allocator()); if (!r.has_value) { fprintf(stderr, "%s\n", error_to_string(r.u.error)); } uint8_tensor_t* arr = r.u.value; push_back_uint8_tensor(arr, 42u); return_uint8_tensor(arr);
- Parameters:
capacity – Number of elements to allocate. Must be > 0.
growth – If true, push operations may reallocate the buffer when capacity is reached.
alloc_v – Allocator vtable for all memory operations. alloc_v.allocate must not be NULL.
- Returns:
uint8_tensor_expect_t with has_value true on success. On failure has_value is false and u.error is set.
Push Operations
-
static inline error_code_t push_back_uint8_array(uint8_tensor_t *t, uint8_t data)
Append one element to the back of a dynamic 1-D array.
Increments len by one after copying the value. If the array is full and growth is true the buffer is reallocated automatically. Returns CAPACITY_OVERFLOW if the array is full and growth is false, or if the allocator does not support reallocation. Returns NULL_POINTER if t is NULL, PRECONDITION_FAIL if mode != ARRAY_STRUCT.
push_back_uint8_tensor(arr, 10u); push_back_uint8_tensor(arr, 20u); // arr = [10, 20], len == 2
-
static inline error_code_t push_front_uint8_array(uint8_tensor_t *t, uint8_t data)
Prepend one element to the front of a dynamic 1-D array.
Shifts all existing elements one slot toward the back then copies the new value into slot 0. O(n) due to the shift. Returns NULL_POINTER if t is NULL, PRECONDITION_FAIL if mode != ARRAY_STRUCT.
push_back_uint8_tensor(arr, 10u); push_front_uint8_tensor(arr, 99u); // arr = [99, 10]
-
static inline error_code_t push_at_uint8_array(uint8_tensor_t *t, uint8_t data, size_t index)
Insert one element at an arbitrary index in a dynamic 1-D array.
Shifts elements at positions [index, len) one slot toward the back then copies the new value into the vacated slot. Valid index range is [0, len]. index == 0 is equivalent to push_front, index == len is equivalent to push_back. O(n) due to the shift. Returns NULL_POINTER if t is NULL, OUT_OF_BOUNDS if index > len, PRECONDITION_FAIL if mode != ARRAY_STRUCT.
// arr = [10, 20, 30] push_at_uint8_tensor(arr, 99u, 1u); // arr = [10, 99, 20, 30]
Pop Operations
-
static inline error_code_t pop_back_uint8_array(uint8_tensor_t *t, uint8_t *out)
Remove and optionally retrieve the last element of a dynamic 1-D array.
Decrements len by one. If out is non-NULL, copies the removed value into the caller-provided buffer. Pass NULL for out to discard the value. Returns NULL_POINTER if t is NULL, EMPTY if len == 0, PRECONDITION_FAIL if mode != ARRAY_STRUCT.
// arr = [10, 20, 30] uint8_t val; pop_back_uint8_tensor(arr, &val); // val == 30, arr = [10, 20] pop_back_uint8_tensor(arr, NULL); // discard, arr = [10]
-
static inline error_code_t pop_front_uint8_array(uint8_tensor_t *t, uint8_t *out)
Remove and optionally retrieve the first element of a dynamic 1-D array.
Copies the first element into out (if non-NULL), then shifts all remaining elements one slot toward the front and decrements len. O(n) due to the shift. Returns NULL_POINTER if t is NULL, EMPTY if len == 0, PRECONDITION_FAIL if mode != ARRAY_STRUCT.
// arr = [10, 20, 30] uint8_t val; pop_front_uint8_tensor(arr, &val); // val == 10, arr = [20, 30]
-
static inline error_code_t pop_at_uint8_array(uint8_tensor_t *t, uint8_t *out, size_t index)
Remove and optionally retrieve the element at an arbitrary index in a dynamic 1-D array.
Copies the element at index into out (if non-NULL), shifts all elements after index one slot toward the front, and decrements len. index == 0 delegates to pop_front, index == len - 1 delegates to pop_back. O(n) due to the shift. Returns NULL_POINTER if t is NULL, EMPTY if len == 0, OUT_OF_BOUNDS if index >= len, PRECONDITION_FAIL if mode != ARRAY_STRUCT.
// arr = [10, 20, 30, 40] uint8_t val; pop_at_uint8_tensor(arr, &val, 2u); // val == 30, arr = [10, 20, 40]
Utility Operations
-
static inline error_code_t concat_uint8_tensor_array(uint8_tensor_t *dst, const uint8_tensor_t *src)
Append all elements of src to the back of dst.
Copies src->len elements from src’s data buffer into the next available slots in dst’s data buffer and increments dst->len accordingly. Both tensors must be in ARRAY_STRUCT mode. If dst is full and growth is true the buffer is reallocated once to cover all of src rather than element-by-element. Returns NULL_POINTER if dst or src is NULL.
uint8_tensor_expect_t r1 = init_uint8_array(8, false, heap_allocator()); uint8_tensor_expect_t r2 = init_uint8_array(4, false, heap_allocator()); uint8_tensor_t* dst = r1.u.value; uint8_tensor_t* src = r2.u.value; push_back_uint8_tensor(dst, 1u); push_back_uint8_tensor(src, 2u); push_back_uint8_tensor(src, 3u); concat_uint8_tensor_array(dst, src); // dst = [1, 2, 3] return_uint8_tensor(dst); return_uint8_tensor(src);