ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_export_zip.c
Go to the documentation of this file.
1
13
14#include <stdio.h>
15#include <string.h>
16
17#include "mdl_export.h"
18#include "mdl_export_internal.h"
19#include "mdl_urlname.h"
20#include "miniz.h"
21#include "ra8_attributes.h"
22
38RA8_INTERNAL static bool internal_zip_align_size(size_t bytes, size_t* out)
39{
40 const size_t alignment = _Alignof(max_align_t);
41 const size_t mask = alignment - 1U;
42 if ((out == nullptr) || (bytes > (SIZE_MAX - mask))) {
43 return false;
44 }
45 *out = (bytes + mask) & ~mask;
46 return true;
47}
48
66 const void* address)
67{
68 if ((alloc == nullptr) || (alloc->ws == nullptr) || (address == nullptr)) {
69 return nullptr;
70 }
71 uint8_t* const end = alloc->ws->data + alloc->ws->used;
72 for (mdl_zip_block_t* block = alloc->first;
73 (block != nullptr) && ((uint8_t*)block < end) && (block->span >= sizeof(*block));
74 block = ((uint8_t*)block + block->span < end)
75 ? (mdl_zip_block_t*)((uint8_t*)block + block->span)
76 : nullptr) {
77 if ((const void*)(block + 1) == address) {
78 return block;
79 }
80 }
81 return nullptr;
82}
83
101RA8_INTERNAL static void* internal_zip_workspace_alloc(void* opaque, size_t items, size_t size)
102{
104 if ((alloc == nullptr) || (alloc->ws == nullptr) || (items == 0U) || (size == 0U) ||
105 (items > (SIZE_MAX / size))) {
106 if (alloc != nullptr) {
107 alloc->exhausted = true;
108 }
109 return nullptr;
110 }
111 const size_t bytes = items * size;
112 size_t payload_span;
113 if (!internal_zip_align_size(bytes, &payload_span) ||
114 (payload_span > (SIZE_MAX - sizeof(mdl_zip_block_t)))) {
115 alloc->exhausted = true;
116 return nullptr;
117 }
118 uint8_t* const end = alloc->ws->data + alloc->ws->used;
119 for (mdl_zip_block_t* block = alloc->first;
120 (block != nullptr) && ((uint8_t*)block < end) && (block->span >= sizeof(*block));
121 block = ((uint8_t*)block + block->span < end)
122 ? (mdl_zip_block_t*)((uint8_t*)block + block->span)
123 : nullptr) {
124 if (block->free && ((block->span - sizeof(*block)) >= bytes)) {
125 block->bytes = bytes;
126 block->free = false;
127 return block + 1;
128 }
129 }
130 const size_t block_span = sizeof(mdl_zip_block_t) + payload_span;
131 mdl_zip_block_t* block =
132 (mdl_zip_block_t*)mdl_export_workspace_take(alloc->ws, block_span, _Alignof(max_align_t));
133 if (block == nullptr) {
134 alloc->exhausted = true;
135 return nullptr;
136 }
137 block->span = block_span;
138 block->bytes = bytes;
139 block->free = false;
140 if (alloc->first == nullptr) {
141 alloc->first = block;
142 }
143 return block + 1;
144}
145
159RA8_INTERNAL static void internal_zip_workspace_free(void* opaque, void* address)
160{
162 mdl_zip_block_t* block = internal_zip_find_block(alloc, address);
163 if (block != nullptr) {
164 block->free = true;
165 }
166}
167
186RA8_INTERNAL static void*
187internal_zip_workspace_realloc(void* opaque, void* address, size_t items, size_t size)
188{
190 if (address == nullptr) {
191 return internal_zip_workspace_alloc(opaque, items, size);
192 }
193 if ((alloc == nullptr) || (items == 0U) || (size == 0U) || (items > (SIZE_MAX / size))) {
194 if (alloc != nullptr) {
195 alloc->exhausted = true;
196 }
197 return nullptr;
198 }
199 mdl_zip_block_t* block = internal_zip_find_block(alloc, address);
200 const size_t bytes = items * size;
201 if ((block == nullptr) || block->free) {
202 alloc->exhausted = true;
203 return nullptr;
204 }
205 if ((block->span - sizeof(*block)) >= bytes) {
206 block->bytes = bytes;
207 return address;
208 }
209 void* replacement = internal_zip_workspace_alloc(opaque, items, size);
210 if (replacement == nullptr) {
211 return nullptr;
212 }
213 memcpy(replacement, address, block->bytes);
214 block->free = true;
215 return replacement;
216}
217
232RA8_PRIV void priv_mdl_zip_workspace_bind(mz_zip_archive* zip,
233 mdl_zip_allocator_t* alloc,
235{
236 memset(zip, 0, sizeof(*zip));
237 *alloc = (mdl_zip_allocator_t){.ws = ws, .floor = ws->used};
238 zip->m_pAlloc = internal_zip_workspace_alloc;
239 zip->m_pFree = internal_zip_workspace_free;
240 zip->m_pRealloc = internal_zip_workspace_realloc;
241 zip->m_pAlloc_opaque = alloc;
242}
243
257{
258 if ((alloc != nullptr) && (alloc->ws != nullptr)) {
259 alloc->ws->used = alloc->floor;
260 }
261}
262
279{
280 return ((alloc != nullptr) && alloc->exhausted) ? k_ra8_err_invalid_size : k_ra8_fail;
281}
282
306 const mdl_export_meta_t* meta,
307 const char names[][k_name_max],
308 size_t count,
310{
311 memset(cover, 0, sizeof(*cover));
312 if ((meta == nullptr) || (meta->cover_path[0] == '\0')) {
313 return k_ra8_ok;
314 }
315 if (strnlen(meta->cover_path, sizeof(meta->cover_path)) == sizeof(meta->cover_path)) {
317 }
318 for (size_t i = 0U; i < count; ++i) {
319 if (strcmp(meta->cover_path, names[i]) == 0) {
320 return k_ra8_ok;
321 }
322 }
323
324 fw_fs_stat_t stat = {};
325 const ra8_err_t err = fw_fs_stat(&storage->fs->names, meta->cover_path, &stat);
326 if ((err != k_ra8_ok) || !stat.exists || (stat.type != k_fw_fs_node_file) ||
327 (stat.size_bytes == 0U)) {
329 }
330 char ext[k_cover_ext_bytes];
331 if (mdl_urlname_sniff_file(storage,
332 meta->cover_path,
333 nullptr,
334 ext,
335 sizeof(ext),
336 cover->mime,
337 sizeof(cover->mime)) != k_ra8_ok) {
339 }
340 const int written = snprintf(cover->entry, sizeof(cover->entry), "cover/cover.%s", ext);
341 if (!priv_mdl_export_snprintf_fit(written, sizeof(cover->entry))) {
343 }
344 cover->source = meta->cover_path;
345 cover->external = true;
346 return k_ra8_ok;
347}
348
369 mdl_storage_t* storage,
370 const char* dir,
371 char names[][k_name_max],
372 size_t count)
373{
374 for (size_t i = 0U; i < count; ++i) {
375 char source[k_fw_fs_path_cap];
376 ra8_err_t err = priv_mdl_export_path_join(source, sizeof(source), dir, names[i]);
377 if (err == k_ra8_ok) {
378 err = priv_mdl_export_zip_add_file(zip, storage, names[i], source, MZ_NO_COMPRESSION);
379 }
380 if (err != k_ra8_ok) {
381 return err;
382 }
383 }
384 return k_ra8_ok;
385}
386
407 const mdl_export_meta_t* meta,
408 size_t count,
409 const mdl_external_cover_t* cover,
410 const mdl_zip_allocator_t* allocator)
411{
412 mdl_export_meta_t cover_meta;
413 const mdl_export_meta_t* comic_meta = meta;
414 size_t comic_pages = count;
415 if (cover->external) {
416 cover_meta = *meta;
417 cover_meta.cover_index = 0;
418 comic_meta = &cover_meta;
419 ++comic_pages;
420 }
421 char comic_xml[4096];
422 const ra8_err_t rc =
423 mdl_export_build_comicinfo_pages(comic_meta, comic_pages, comic_xml, sizeof(comic_xml));
424 if (rc != k_ra8_ok) {
425 return rc;
426 }
428 "ComicInfo.xml",
429 (const uint8_t*)comic_xml,
430 strlen(comic_xml),
431 MZ_NO_COMPRESSION);
432 return (added == k_ra8_ok) ? k_ra8_ok : priv_mdl_zip_workspace_error(allocator);
433}
434
459 const char* dir,
460 char names[][k_name_max],
461 size_t count,
462 mdl_export_output_t* output,
463 const mdl_export_meta_t* meta,
465{
467 ra8_err_t rc = priv_mdl_export_prepare_cover(storage, meta, names, count, &cover);
468 if (rc != k_ra8_ok) {
469 return rc;
470 }
471 mz_zip_archive zip;
472 mdl_zip_allocator_t zip_alloc;
473 priv_mdl_zip_workspace_bind(&zip, &zip_alloc, ws);
474 zip.m_pWrite = priv_mdl_export_zip_write;
475 zip.m_pIO_opaque = output;
476 if (mz_zip_writer_init(&zip, 0) == MZ_FALSE) {
477 const ra8_err_t zip_rc = priv_mdl_zip_workspace_error(&zip_alloc);
479 return zip_rc;
480 }
481 if (cover.external) {
482 rc = priv_mdl_export_zip_add_file(&zip, storage, cover.entry, cover.source, MZ_NO_COMPRESSION);
483 if ((rc == k_ra8_fail) && zip_alloc.exhausted) {
485 }
486 }
487 if (rc == k_ra8_ok) {
488 rc = internal_cbz_add_pages(&zip, storage, dir, names, count);
489 if ((rc == k_ra8_fail) && zip_alloc.exhausted) {
491 }
492 }
493 if (rc == k_ra8_ok) {
494 rc = internal_cbz_add_metadata(&zip, meta, count, &cover, &zip_alloc);
495 }
496 if ((rc == k_ra8_ok) && (mz_zip_writer_finalize_archive(&zip) == MZ_FALSE)) {
497 rc = priv_mdl_zip_workspace_error(&zip_alloc);
498 }
499 (void)mz_zip_writer_end(&zip); /* alloc-allow: releases caller-arena blocks */
501 if ((rc == k_ra8_fail) && (output->error != k_ra8_ok)) {
502 rc = output->error;
503 }
504 return rc;
505}
ra8_err_t fw_fs_stat(const fw_fs_namespace_t *names, const char *path, fw_fs_stat_t *out)
Query a path; a miss is success with out->exists == false.
Definition fw_if_fs.c:480
@ k_fw_fs_node_file
Regular byte stream.
@ k_fw_fs_path_cap
Largest portable path including its NUL.
Package a downloaded chapter folder into a reader-openable container.
void * mdl_export_workspace_take(mdl_export_workspace_t *ws, size_t bytes, size_t alignment)
Reserve aligned bytes from an exporter arena.
struct mdl_export_workspace mdl_export_workspace_t
Caller-owned bounded arena for all exporter scratch state.
ra8_err_t mdl_export_build_comicinfo_pages(const mdl_export_meta_t *meta, size_t page_count, char *buf, size_t cap)
Generate ComicInfo.xml including page count and direction semantics.
Module-private contracts shared by chapter export writers.
@ k_cover_ext_bytes
Canonical image extension buffer.
@ k_name_max
Maximum entry-name bytes.
bool priv_mdl_export_snprintf_fit(int written, size_t cap)
Report whether an snprintf result fit its destination.
ra8_err_t priv_mdl_export_zip_add_file(mz_zip_archive *zip, mdl_storage_t *storage, const char *member, const char *path, mz_uint flags)
Add one portable source file through miniz callbacks.
ra8_err_t priv_mdl_export_path_join(char *out, size_t capacity, const char *directory, const char *leaf)
Join a canonical directory and leaf without truncation.
size_t priv_mdl_export_zip_write(void *opaque, mz_uint64 file_offset, const void *bytes, size_t length)
Adapt random-offset miniz output to the active export stage.
ra8_err_t priv_mdl_export_zip_add_memory(mz_zip_archive *zip, const char *member, const uint8_t *bytes, size_t length, mz_uint flags)
Add caller-owned memory through the deterministic ZIP read seam.
ra8_err_t priv_mdl_export_cbz(mdl_storage_t *storage, const char *dir, char names[][k_name_max], size_t count, mdl_export_output_t *output, const mdl_export_meta_t *meta, mdl_export_workspace_t *ws)
Write a CBZ with pages, canonical cover, and ComicInfo metadata.
static void * internal_zip_workspace_realloc(void *opaque, void *address, size_t items, size_t size)
Resize a miniz block within the bounded reusable arena.
ra8_err_t priv_mdl_zip_workspace_error(const mdl_zip_allocator_t *alloc)
Map a miniz failure to bounded exhaustion when applicable.
static void internal_zip_workspace_free(void *opaque, void *address)
Release a miniz block for reuse within the current writer.
void priv_mdl_zip_workspace_bind(mz_zip_archive *zip, mdl_zip_allocator_t *alloc, mdl_export_workspace_t *ws)
Bind one zeroed miniz archive to a bounded allocator.
static ra8_err_t internal_cbz_add_metadata(mz_zip_archive *zip, const mdl_export_meta_t *meta, size_t count, const mdl_external_cover_t *cover, const mdl_zip_allocator_t *allocator)
Build and append ComicInfo metadata to a CBZ writer.
static bool internal_zip_align_size(size_t bytes, size_t *out)
Round a byte count up to maximum host alignment.
void priv_mdl_zip_workspace_release(mdl_zip_allocator_t *alloc)
Release all miniz allocations while preserving high-water.
static void * internal_zip_workspace_alloc(void *opaque, size_t items, size_t size)
Allocate reusable miniz storage from a bounded exporter arena.
static mdl_zip_block_t * internal_zip_find_block(mdl_zip_allocator_t *alloc, const void *address)
Find an allocator block by its payload address.
ra8_err_t priv_mdl_export_prepare_cover(mdl_storage_t *storage, const mdl_export_meta_t *meta, const char names[][k_name_max], size_t count, mdl_external_cover_t *cover)
Validate and canonicalize a cover not already present as a page.
static ra8_err_t internal_cbz_add_pages(mz_zip_archive *zip, mdl_storage_t *storage, const char *dir, char names[][k_name_max], size_t count)
Add every discovered page to an initialized CBZ writer.
Bounded URL naming and portable image-classification helpers.
ra8_err_t mdl_urlname_sniff_file(mdl_storage_t *storage, const char *file_path, const char *content_type, char *out_ext, size_t ext_cap, char *out_mime, size_t mime_cap)
Sniff true image extension and MIME type from portable storage.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_fail
Generic unspecified failure.
Definition ra8_err.h:133
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ 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
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
size_t strnlen(const char *s, size_t maxlen)
Calculate bounded string length.
Result of a portable metadata query.
uint64_t size_bytes
File length; zero for a directory.
bool exists
False means a clean lookup miss.
fw_fs_node_type_t type
Kind of node at the path.
fw_fs_namespace_t names
Namespace operations.
Rich series and chapter metadata for export containers (ComicInfo.xml, EPUB OPF).
Definition mdl_export.h:96
int cover_index
Cover page index (0-based, -1 if unset).
Definition mdl_export.h:105
char cover_path[k_mdl_meta_path_max]
Trusted verified local cover path/name.
Definition mdl_export.h:104
One validated staged publication, including random ZIP backfill.
ra8_err_t error
First callback error, if any.
size_t used
Current allocation high edge.
Definition mdl_export.h:220
uint8_t * data
Writable arena bytes.
Definition mdl_export.h:218
Validated external cover details retained without owning storage.
const char * source
Trusted caller-owned source path.
char entry[k_cover_entry_bytes]
Canonical relative member path.
char mime[k_cover_mime_bytes]
MIME derived from magic bytes.
bool external
True when a separate cover exists.
One non-reentrant downloader filesystem dependency bundle.
Definition mdl_storage.h:40
const fw_fs_t * fs
Injected portable filesystem.
Definition mdl_storage.h:41
Per-writer adapter from miniz callbacks to one caller workspace.
size_t floor
Cursor restored after writer end.
mdl_export_workspace_t * ws
Exclusive caller-owned workspace.
mdl_zip_block_t * first
First miniz block, or NULL.
bool exhausted
A callback exceeded bounded space.
Metadata stored immediately before each bounded miniz allocation.
size_t bytes
Requested payload bytes currently preserved.
bool free
Whether this block can satisfy a later request.
size_t span
Aligned header-plus-payload bytes in this block.