ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
epub_miniz_alloc.c
Go to the documentation of this file.
1
14
15#include "epub_miniz_alloc.h"
16
17#include <stddef.h>
18#include <stdint.h>
19#include <string.h>
20
21#include "ra8_attributes.h"
22
24typedef struct {
25 size_t size;
26 size_t is_free;
28
30typedef enum : size_t {
31 k_priv_hdr_bytes = ((sizeof(priv_blk_t) + alignof(max_align_t) - 1U) / alignof(max_align_t)) *
32 alignof(max_align_t),
33 k_priv_align = alignof(max_align_t),
35 k_epub_miniz_pool_bytes / (k_priv_hdr_bytes + alignof(max_align_t)),
39
40static_assert((k_epub_miniz_pool_bytes % alignof(max_align_t)) == 0U,
41 "miniz workspace size must be a whole number of aligned cells");
42static_assert(sizeof(epub_miniz_workspace_t) == k_epub_miniz_pool_bytes,
43 "public miniz workspace must have exact advertised capacity");
44
60static uintptr_t internal_pointer_address(const void* pointer)
61{
62 uintptr_t address = 0U;
63 static_assert(sizeof(address) == sizeof(pointer), "uintptr_t must represent object pointers");
64 (void)memcpy((void*)&address, (const void*)&pointer, sizeof(address));
65 return address;
66}
67
83static bool internal_ready(const epub_miniz_arena_t* arena)
84{
85 if (arena == nullptr) {
86 return false;
87 }
88 if (arena->initialized != 1U) {
89 return false;
90 }
91 if (arena->base == nullptr) {
92 return false;
93 }
94 if (arena->capacity != (size_t)k_epub_miniz_pool_bytes) {
95 return false;
96 }
97 return (internal_pointer_address(arena->base) % alignof(max_align_t)) == 0U;
98}
99
107static priv_blk_t* internal_cell_at(const epub_miniz_arena_t* arena, size_t off)
108{
109 void* const cell = &arena->base[off];
110 return (priv_blk_t*)cell;
111}
112
115static uint8_t* internal_end(const epub_miniz_arena_t* arena)
116{
117 return &arena->base[arena->capacity];
118}
119
122static uint8_t* internal_payload(priv_blk_t* block)
123{
124 return &((uint8_t*)block)[(size_t)k_priv_hdr_bytes];
125}
126
129static priv_blk_t* internal_header(const epub_miniz_arena_t* arena, void* address)
130{
131 const uintptr_t payload = internal_pointer_address(address);
132 const uintptr_t base = internal_pointer_address(arena->base);
133 return internal_cell_at(arena, (size_t)(payload - base) - (size_t)k_priv_hdr_bytes);
134}
135
139{
140 const uintptr_t end = internal_pointer_address(&internal_payload(block)[block->size]);
141 const uintptr_t base = internal_pointer_address(arena->base);
142 return internal_cell_at(arena, (size_t)(end - base));
143}
144
162static bool internal_in_pool(const epub_miniz_arena_t* arena, const void* address)
163{
164 if (!internal_ready(arena)) {
165 return false;
166 }
167 const uintptr_t value = internal_pointer_address(address);
168 const uintptr_t base = internal_pointer_address(arena->base);
169 const uintptr_t first = base + (uintptr_t)k_priv_hdr_bytes;
170 const uintptr_t end = base + (uintptr_t)arena->capacity;
171 return (value >= first) && (value < end);
172}
173
190static size_t internal_align_up(size_t bytes)
191{
192 const size_t mask = (size_t)k_priv_align - 1U;
193 return (bytes + mask) & ~mask;
194}
195
214static bool internal_request_bytes(size_t items, size_t size, size_t* out_bytes)
215{
216 if ((size != 0U) && (items > (SIZE_MAX / size))) {
217 return false;
218 }
219 const size_t bytes = items * size;
220 if (bytes > (size_t)k_epub_miniz_pool_bytes) {
221 return false;
222 }
223 *out_bytes = bytes;
224 return true;
225}
226
242static void internal_split(const epub_miniz_arena_t* arena, priv_blk_t* block, size_t need)
243{
244 const size_t remainder_min = (size_t)k_priv_hdr_bytes + (size_t)k_priv_align;
245 if ((block->size - need) < remainder_min) {
246 return;
247 }
248 const uint8_t* const payload = internal_payload(block);
249 const uintptr_t rem_addr = internal_pointer_address(payload) + need;
250 const uintptr_t base = internal_pointer_address(arena->base);
251 priv_blk_t* const remainder = internal_cell_at(arena, (size_t)(rem_addr - base));
252 remainder->size = block->size - need - (size_t)k_priv_hdr_bytes;
253 remainder->is_free = (size_t)k_priv_blk_free;
254 block->size = need;
255}
256
270static void internal_coalesce(const epub_miniz_arena_t* arena)
271{
272 priv_blk_t* block = internal_cell_at(arena, 0U);
273 for (size_t guard = 0U; guard < (size_t)k_priv_walk_max; ++guard) {
274 if ((uint8_t*)block >= internal_end(arena)) {
275 break;
276 }
277 if (block->is_free == (size_t)k_priv_blk_free) {
278 priv_blk_t* next = internal_next(arena, block);
279 while (((uint8_t*)next < internal_end(arena)) && (next->is_free == (size_t)k_priv_blk_free)) {
280 block->size += (size_t)k_priv_hdr_bytes + next->size;
281 next = internal_next(arena, block);
282 }
283 }
284 block = internal_next(arena, block);
285 }
286}
287
288ra8_err_t epub_miniz_arena_init(epub_miniz_arena_t* arena, void* workspace, size_t workspace_bytes)
289{
290 if ((arena == nullptr) || (workspace == nullptr)) {
291 return k_ra8_err_null_ptr;
292 }
293 if (workspace_bytes < (size_t)k_epub_miniz_pool_bytes) {
295 }
296 if ((internal_pointer_address(workspace) % alignof(max_align_t)) != 0U) {
298 }
299 const epub_miniz_arena_t next = {
300 .base = (uint8_t*)workspace,
301 .capacity = (size_t)k_epub_miniz_pool_bytes,
302 .initialized = 1U,
303 };
304 priv_blk_t* const head = internal_cell_at(&next, 0U);
305 head->size = next.capacity - (size_t)k_priv_hdr_bytes;
306 head->is_free = (size_t)k_priv_blk_free;
307 *arena = next;
308 return k_ra8_ok;
309}
310
312{
313 if (arena != nullptr) {
314 *arena = (epub_miniz_arena_t){};
315 }
316}
317
318void* epub_miniz_alloc(void* opaque, size_t items, size_t size)
319{
320 epub_miniz_arena_t* const arena = (epub_miniz_arena_t*)opaque;
321 if (!internal_ready(arena)) {
322 return nullptr;
323 }
324 size_t bytes = 0U;
325 if (!internal_request_bytes(items, size, &bytes)) {
326 return nullptr;
327 }
328 size_t need = internal_align_up(bytes);
329 if (need == 0U) {
330 need = (size_t)k_priv_align;
331 }
332 priv_blk_t* block = internal_cell_at(arena, 0U);
333 for (size_t guard = 0U; guard < (size_t)k_priv_walk_max; ++guard) {
334 if ((uint8_t*)block >= internal_end(arena)) {
335 break;
336 }
337 if ((block->is_free == (size_t)k_priv_blk_free) && (block->size >= need)) {
338 internal_split(arena, block, need);
339 block->is_free = (size_t)k_priv_blk_used;
340 return internal_payload(block);
341 }
342 block = internal_next(arena, block);
343 }
344 return nullptr;
345}
346
347void epub_miniz_free(void* opaque, void* address)
348{
349 epub_miniz_arena_t* const arena = (epub_miniz_arena_t*)opaque;
350 if ((address == nullptr) || !internal_in_pool(arena, address)) {
351 return;
352 }
353 priv_blk_t* const block = internal_header(arena, address);
354 block->is_free = (size_t)k_priv_blk_free;
355 internal_coalesce(arena);
356}
357
358void* epub_miniz_realloc(void* opaque, void* address, size_t items, size_t size)
359{
360 if (address == nullptr) {
361 return epub_miniz_alloc(opaque, items, size);
362 }
363 epub_miniz_arena_t* const arena = (epub_miniz_arena_t*)opaque;
364 if (!internal_in_pool(arena, address)) {
365 return nullptr;
366 }
367 size_t bytes = 0U;
368 if (!internal_request_bytes(items, size, &bytes)) {
369 return nullptr;
370 }
371 const size_t need = internal_align_up(bytes);
372 if (need == 0U) {
373 epub_miniz_free(arena, address);
374 return nullptr;
375 }
376 priv_blk_t* const block = internal_header(arena, address);
377 if (block->size >= need) {
378 return address;
379 }
380 void* const moved = epub_miniz_alloc(arena, items, size);
381 if (moved == nullptr) {
382 return nullptr;
383 }
384 (void)memcpy(moved, address, block->size);
385 epub_miniz_free(arena, address);
386 return moved;
387}
priv_alloc_const_t
Private allocator geometry and sentinel values.
@ k_priv_hdr_bytes
Aligned in-band header bytes.
@ k_priv_blk_used
Live-block marker.
@ k_priv_align
Maximum-alignment cell size.
@ k_priv_walk_max
Block-walk guard.
@ k_priv_blk_free
Free-block marker.
static bool internal_ready(const epub_miniz_arena_t *arena)
Validate an arena descriptor before using caller-owned storage.
void epub_miniz_free(void *opaque, void *address)
miniz-compatible free (mz_free_func).
ra8_err_t epub_miniz_arena_init(epub_miniz_arena_t *arena, void *workspace, size_t workspace_bytes)
Initialise or reset a miniz arena over caller-owned storage.
static size_t internal_align_up(size_t bytes)
Align a pool-bounded request upward to a complete arena cell.
static uint8_t * internal_end(const epub_miniz_arena_t *arena)
Return the arena's one-past byte sentinel.
static void internal_coalesce(const epub_miniz_arena_t *arena)
Merge all adjacent free blocks in an arena.
void * epub_miniz_alloc(void *opaque, size_t items, size_t size)
miniz-compatible allocator (mz_alloc_func).
static priv_blk_t * internal_cell_at(const epub_miniz_arena_t *arena, size_t off)
Cast byte offset off to an aligned block header.
void * epub_miniz_realloc(void *opaque, void *address, size_t items, size_t size)
miniz-compatible realloc (mz_realloc_func).
static void internal_split(const epub_miniz_arena_t *arena, priv_blk_t *block, size_t need)
Split a block if its remainder can hold another allocation.
static uintptr_t internal_pointer_address(const void *pointer)
Copy a pointer representation into an integer address.
static bool internal_request_bytes(size_t items, size_t size, size_t *out_bytes)
Validate and multiply a miniz count/size request exactly once.
static priv_blk_t * internal_next(const epub_miniz_arena_t *arena, priv_blk_t *block)
Return the block immediately following block.
void epub_miniz_arena_deinit(epub_miniz_arena_t *arena)
Invalidate an arena descriptor after miniz has released its blocks.
static bool internal_in_pool(const epub_miniz_arena_t *arena, const void *address)
Test whether an address lies in the payload-bearing arena range.
static priv_blk_t * internal_header(const epub_miniz_arena_t *arena, void *address)
Return the header for an allocation payload.
static uint8_t * internal_payload(priv_blk_t *block)
Return the payload immediately following block.
Caller-owned bounded-arena allocator for miniz.
@ k_epub_miniz_pool_bytes
Arena size, bytes (160 KiB).
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Descriptor for one caller-owned miniz allocation arena.
size_t capacity
Usable bytes; exactly the documented pool size.
uint8_t initialized
1 after successful init; 0 after deinit.
uint8_t * base
Aligned caller workspace, or NULL when inactive.
Exactly-sized, maximally-aligned storage for one miniz arena.
Header prefixing every arena block.
size_t size
Aligned payload bytes after this header.
size_t is_free
One for free, zero for live.