ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
comic_cbz.c
Go to the documentation of this file.
1
27#include <stddef.h>
28#include <string.h>
29
30#include "comic.h"
31#include "comic_internal.h"
32#include "epub_miniz_alloc.h"
33#include "miniz.h"
34#include "ra8_attributes.h"
35#include "ra8_check.h"
36#include "ra8_decomp_limits.h"
37
39static const char* const s_tag_cbz = "comic_cbz";
40
41/* The comic record holds the mz_zip_archive inline (no heap); prove the opaque
42 * byte buffer is large enough and aligned for miniz's reader state. */
43static_assert(k_comic_zip_bytes >= sizeof(mz_zip_archive),
44 "k_comic_zip_bytes too small for mz_zip_archive");
45static_assert(alignof(mz_zip_archive) <= alignof(max_align_t),
46 "mz_zip_archive alignment exceeds max_align_t");
47
53typedef enum : uint16_t {
56
68RA8_INTERNAL static mz_zip_archive* internal_zip(comic_t* c)
69{
70 /* Reinterpret the aligned byte arena as the archive via a `void*` seam (as
71 * `epub` does): a direct `uint8_t* -> mz_zip_archive*` cast would trip
72 * -Wcast-align, and a one-expression `(T*)(void*)p` cast trips
73 * bugprone-casting-through-void, so the `void*` is a named local. */
74 void* const storage = &c->zip_storage[0];
75 return (mz_zip_archive*)storage;
76}
77
96RA8_INTERNAL static size_t
97internal_stream_read(void* opaque, mz_uint64 file_ofs, void* buf, size_t n)
98{
99 const comic_stream_t* sm = (const comic_stream_t*)opaque;
100 if (sm == nullptr) {
101 return 0U; /* GCOVR_EXCL_LINE -- open binds a live stream; miniz never passes NULL */
102 }
103 if (sm->read == nullptr) {
104 return 0U; /* GCOVR_EXCL_LINE -- open validates a non-NULL reader */
105 }
106 if (file_ofs >= sm->size) {
107 return 0U; /* GCOVR_EXCL_LINE -- miniz clamps reads within the archive size */
108 }
109 const uint64_t avail = sm->size - file_ofs;
110 const size_t want = ((uint64_t)n > avail) ? (size_t)avail : n;
111 return sm->read(sm->ctx, (uint64_t)file_ofs, buf, want);
112}
113
130RA8_INTERNAL static ra8_err_t internal_set_alloc(comic_t* c, mz_zip_archive* zip)
131{
133 &c->miniz_workspace.bytes[0],
134 sizeof(c->miniz_workspace.bytes));
135 if (err != k_ra8_ok) {
136 return err; /* GCOVR_EXCL_LINE -- embedded workspace is exact and aligned */
137 }
138 zip->m_pAlloc = epub_miniz_alloc;
139 zip->m_pFree = epub_miniz_free;
140 zip->m_pRealloc = epub_miniz_realloc;
141 zip->m_pAlloc_opaque = &c->miniz_arena;
142 return k_ra8_ok;
143}
144
163RA8_INTERNAL static ra8_err_t internal_add_entry(comic_t* c, mz_zip_archive* zip, mz_uint i)
164{
165 if (mz_zip_reader_is_file_a_directory(zip, i) != MZ_FALSE) {
166 return k_ra8_ok;
167 }
168 char nb[k_cbz_name_max] = {};
169 (void)mz_zip_reader_get_filename(zip, i, nb, (mz_uint)sizeof(nb));
170 nb[sizeof(nb) - 1U] = '\0';
171 size_t nl = 0U;
172 for (; nl < sizeof(nb); ++nl) {
173 if (nb[nl] == '\0') {
174 break;
175 }
176 }
177 if (!priv_comic_is_page_name(nb, (uint16_t)nl)) {
178 return k_ra8_ok;
179 }
180 mz_zip_archive_file_stat st = {};
181 if (mz_zip_reader_file_stat(zip, i, &st) == MZ_FALSE) {
182 return k_ra8_ok; /* GCOVR_EXCL_LINE -- stat cannot fail on an index locate already found */
183 }
184 /* Decompression-limits guard: a page whose central-directory record
185 * declares an over-cap or bomb-ratio size rejects the whole archive
186 * before any inflation (the same policy every decoder enforces). */
188 const ra8_err_t derr =
189 ra8_decomp_check_declared(&lim, (uint64_t)st.m_comp_size, (uint64_t)st.m_uncomp_size);
190 if (derr != k_ra8_ok) {
191 return derr;
192 }
193 return priv_comic_page_add(c,
194 nb,
195 (uint16_t)nl,
196 (uint64_t)st.m_uncomp_size,
197 1U,
198 (uint8_t)k_ra8_rar_method_store, /* method unused by CBZ */
199 (uint32_t)i,
200 0U,
201 0U);
202}
203
205{
206 RA8_CHECK_NULL_PTR(c, s_tag_cbz, "cbz open: null c");
208 if (cap_err != k_ra8_ok) {
209 return cap_err;
210 }
211 mz_zip_archive* zip = internal_zip(c);
212 (void)memset(zip, 0, sizeof(*zip));
213 const ra8_err_t aerr = internal_set_alloc(c, zip);
214 if (aerr != k_ra8_ok) {
215 return aerr; /* GCOVR_EXCL_LINE -- embedded workspace is exact and aligned */
216 }
217 zip->m_pRead = internal_stream_read;
218 zip->m_pIO_opaque = &c->stream;
219 if (mz_zip_reader_init(zip, (mz_uint64)c->size, 0U) == MZ_FALSE) {
222 }
223 c->zip_active = 1U;
224 const mz_uint num = mz_zip_reader_get_num_files(zip);
225 /* Decompression-limits guard: the many-tiny-entries bomb dies before
226 * any central-directory entry is touched. */
228 if ((uint64_t)num > (uint64_t)lim.max_entries) {
229 (void)priv_comic_cbz_close(c);
231 }
232 for (mz_uint i = 0U; i < num; ++i) { /* bound: finite central-directory count */
233 const ra8_err_t e = internal_add_entry(c, zip, i);
234 if (e != k_ra8_ok) {
235 (void)priv_comic_cbz_close(c);
236 return e;
237 }
238 }
239 return k_ra8_ok;
240}
241
243priv_comic_cbz_extract(comic_t* c, const comic_page_t* p, uint8_t* buf, size_t cap, size_t* got)
244{
245 RA8_CHECK_NULL_PTR(c, s_tag_cbz, "cbz extract: null c");
246 RA8_CHECK_NULL_PTR(p, s_tag_cbz, "cbz extract: null p");
247 RA8_CHECK_NULL_PTR(buf, s_tag_cbz, "cbz extract: null buf");
248 RA8_CHECK_NULL_PTR(got, s_tag_cbz, "cbz extract: null got");
249 *got = 0U;
250 if (c->zip_active == 0U) {
252 }
253 if ((uint64_t)cap < p->raw_size) {
254 return k_ra8_err_no_mem;
255 }
256 mz_zip_archive* zip = internal_zip(c);
257 if (mz_zip_reader_extract_to_mem(zip, (mz_uint)p->zip_index, buf, cap, 0U) == MZ_FALSE) {
259 }
260 *got = (size_t)p->raw_size;
261 return k_ra8_ok;
262}
263
265{
266 RA8_CHECK_NULL_PTR(c, s_tag_cbz, "cbz close: null c");
267 if (c->zip_active != 0U) {
268 (void)mz_zip_reader_end(internal_zip(c));
269 c->zip_active = 0U;
270 }
272 return k_ra8_ok;
273}
bool priv_comic_is_page_name(const char *name, uint16_t len)
Whether an archive entry name is a decodable comic page image.
Definition comic.c:152
ra8_err_t priv_comic_page_add(comic_t *c, const char *name, uint16_t name_len, uint64_t raw_size, uint8_t extractable, uint8_t method, uint32_t zip_index, uint64_t data_off, uint64_t pack_size)
Append one image member to the resident page index.
Definition comic.c:177
Unified demand-paged reader for comic-book archives – CBZ (ZIP) and CBR (RAR).
@ k_comic_zip_bytes
Inline storage for miniz's mz_zip_archive.
Definition comic.h:118
cbz_limits_t
Per-entry name scratch size for the central-directory walk.
Definition comic_cbz.c:53
@ k_cbz_name_max
Longest entry name indexed (bytes, incl.
Definition comic_cbz.c:54
static const char *const s_tag_cbz
Log tag for CBZ-backend diagnostics.
Definition comic_cbz.c:39
static size_t internal_stream_read(void *opaque, mz_uint64 file_ofs, void *buf, size_t n)
miniz user-read trampoline – forward to the comic's seek+read backing.
Definition comic_cbz.c:97
static ra8_err_t internal_add_entry(comic_t *c, mz_zip_archive *zip, mz_uint i)
Index one central-directory entry if it is a decodable page image.
Definition comic_cbz.c:163
static mz_zip_archive * internal_zip(comic_t *c)
The comic's inline mz_zip_archive view.
Definition comic_cbz.c:68
ra8_err_t priv_comic_cbz_extract(comic_t *c, const comic_page_t *p, uint8_t *buf, size_t cap, size_t *got)
Extract one CBZ page's encoded image through the streaming ZIP reader.
Definition comic_cbz.c:243
ra8_err_t priv_comic_cbz_close(comic_t *c)
Tear down the CBZ backend's miniz reader.
Definition comic_cbz.c:264
static ra8_err_t internal_set_alloc(comic_t *c, mz_zip_archive *zip)
Route miniz allocations through this comic's private bounded arena.
Definition comic_cbz.c:130
ra8_err_t priv_comic_cbz_open(comic_t *c)
Open the CBZ (ZIP) backend: init the streaming miniz reader + page index.
Definition comic_cbz.c:204
Cross-TU seams between the comic facade and its CBZ / CBR backends.
Caller-owned bounded-arena allocator for miniz.
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.
void * epub_miniz_alloc(void *opaque, size_t items, size_t size)
miniz-compatible allocator (mz_alloc_func).
void * epub_miniz_realloc(void *opaque, void *address, size_t items, size_t size)
miniz-compatible realloc (mz_realloc_func).
void epub_miniz_arena_deinit(epub_miniz_arena_t *arena)
Invalidate an arena descriptor after miniz has released its blocks.
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).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Unified decompression-limits policy: one bound set for every decoder.
ra8_decomp_limits_t ra8_decomp_limits_default(void)
The owner-approved default decompression policy.
ra8_err_t ra8_decomp_check_declared(const ra8_decomp_limits_t *limits, uint64_t comp_size, uint64_t out_size)
Header-level check of a member's declared sizes against a policy.
ra8_err_t ra8_decomp_zip_entry_preflight(ra8_decomp_read_fn read, void *ctx, uint64_t archive_size)
Reject an over-cap ZIP from its EOCD before directory allocation.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ 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_decomp_entries
Archive entry-count cap breached (ra8_decomp_limits_t).
Definition ra8_err.h:509
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
@ k_ra8_rar_method_store
Uncompressed: data area is the file bytes.
Definition ra8_rar.h:112
One entry in the resident, sorted page index.
Definition comic.h:140
uint64_t raw_size
Encoded image length in bytes (decoder input).
Definition comic.h:148
uint32_t zip_index
CBZ: miniz central-directory index.
Definition comic.h:145
Stable seek+read descriptor bound to the CBZ backend's miniz reader.
Definition comic.h:159
uint64_t size
Archive length in bytes.
Definition comic.h:162
void * ctx
Backing context.
Definition comic.h:161
comic_read_fn read
Backing reader (mirrors comic_t::read).
Definition comic.h:160
One open comic archive: backing, kind, page index, and backend state.
Definition comic.h:179
epub_miniz_arena_t miniz_arena
Per-comic allocator descriptor bound through miniz's opaque pointer.
Definition comic.h:198
uint8_t zip_active
1 = miniz reader is initialised (CBZ).
Definition comic.h:194
comic_stream_t stream
CBZ miniz I/O descriptor (stable addr).
Definition comic.h:189
uint8_t zip_storage[k_comic_zip_bytes]
Inline storage for miniz's mz_zip_archive (CBZ; no heap).
Definition comic.h:196
uint64_t size
Archive length in bytes.
Definition comic.h:182
epub_miniz_workspace_t miniz_workspace
Per-comic bounded allocator workspace (used only for CBZ).
Definition comic.h:200
uint8_t bytes[k_epub_miniz_pool_bytes]
Allocator backing bytes.
One decompression policy: the five resource bounds decoders enforce.
uint32_t max_entries
Per-archive enumerated-entry cap.