ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
epub_entry.c
Go to the documentation of this file.
1
33
34#include "epub_entry.h"
35
36#include <stddef.h>
37#include <stdint.h>
38#include <string.h>
39
40#include "epub.h"
41#include "epub_internal.h"
42#include "miniz.h"
43#include "ra8_attributes.h"
44#include "ra8_check.h"
45#include "ra8_err.h"
46
48static const char* const s_tag = "epub_entry";
49
50/* ---------------------------------------------------------------------------
51 * ZIP local-file-header field layout (PKWARE APPNOTE.TXT s4.3.7). These are the
52 * frozen on-disk ZIP format offsets; miniz keeps its equivalents private to
53 * miniz.c, so they are re-declared here rather than reached into the SOUP.
54 * ---------------------------------------------------------------------------
55 */
56
71
76typedef enum : uint8_t {
82
87typedef enum : uint8_t {
91} epub_le_t;
92
93/* ---------------------------------------------------------------------------
94 * Helpers.
95 * ---------------------------------------------------------------------------
96 */
97
111static mz_zip_archive* internal_zip(epub_book_t* book)
112{
113 void* const storage = &book->zip_archive_storage[0];
114 return (mz_zip_archive*)storage;
115}
116
135static ra8_err_t
136internal_locate(mz_zip_archive* zip, epub_book_t* book, const char* path, int32_t* out_idx)
137{
138 char full_path[k_epub_max_path_len];
139 priv_epub_join_path(book->opf_dir, path, full_path, sizeof(full_path));
140 int32_t idx = mz_zip_reader_locate_file(zip, full_path, nullptr, 0U);
141 if (idx < 0) {
142 idx = mz_zip_reader_locate_file(zip, path, nullptr, 0U);
143 }
144 if (idx < 0) {
145 return k_ra8_err_not_found;
146 }
147 *out_idx = idx;
148 return k_ra8_ok;
149}
150
170static bool internal_backing_read(epub_book_t* book, uint64_t archive_ofs, uint8_t* buf, size_t n)
171{
172 if (book->zip_bytes != nullptr) {
173 if ((archive_ofs + (uint64_t)n) > (uint64_t)book->zip_size) {
174 return false; /* GCOVR_EXCL_LINE -- offsets come from a validated central-directory entry */
175 }
176 (void)memcpy(buf, &book->zip_bytes[(size_t)archive_ofs], n);
177 return true;
178 }
179 if (book->stream_media.read == nullptr) {
180 return false; /* GCOVR_EXCL_LINE -- a streamed book always carries a validated read callback */
181 }
182 const size_t got = book->stream_media.read(book->stream_media.ctx, archive_ofs, buf, n);
183 return got == n;
184}
185
204static bool
205internal_data_offset(const uint8_t* hdr, uint64_t local_header_ofs, uint64_t* out_data_ofs)
206{
207 const uint8_t sig[4] = {(uint8_t)k_epub_ldh_sig_0,
208 (uint8_t)k_epub_ldh_sig_1,
209 (uint8_t)k_epub_ldh_sig_2,
210 (uint8_t)k_epub_ldh_sig_3};
211 if (memcmp(hdr, sig, sizeof(sig)) != 0) {
212 return false;
213 }
214 const uint32_t fname =
215 (uint32_t)hdr[(uint8_t)k_epub_ldh_fname_len_ofs + k_epub_le_lo] |
216 ((uint32_t)hdr[(uint8_t)k_epub_ldh_fname_len_ofs + k_epub_le_hi] << k_epub_le_shift);
217 const uint32_t extra =
218 (uint32_t)hdr[(uint8_t)k_epub_ldh_extra_len_ofs + k_epub_le_lo] |
219 ((uint32_t)hdr[(uint8_t)k_epub_ldh_extra_len_ofs + k_epub_le_hi] << k_epub_le_shift);
220 *out_data_ofs = local_header_ofs + (uint64_t)k_epub_ldh_size + (uint64_t)fname + (uint64_t)extra;
221 return true;
222}
223
245 const char* path,
246 mz_zip_reader_extract_iter_state** out_iter,
247 uint64_t* out_uncomp)
248{
249 mz_zip_archive* zip = internal_zip(book);
250 int32_t idx = 0;
251 const ra8_err_t err = internal_locate(zip, book, path, &idx);
252 if (err != k_ra8_ok) {
253 return err;
254 }
255 mz_zip_archive_file_stat st;
256 if (mz_zip_reader_file_stat(zip, (mz_uint)idx, &st) == MZ_FALSE) {
257 return k_ra8_err_validation_failed; /* GCOVR_EXCL_LINE -- stat cannot fail on a located entry */
258 }
259 const ra8_err_t gerr = priv_epub_zip_guard_entry(&st);
260 if (gerr != k_ra8_ok) {
261 return gerr; /* lying header / declared bomb: reject before inflation */
262 }
263 mz_zip_reader_extract_iter_state* iter = mz_zip_reader_extract_iter_new(zip, (mz_uint)idx, 0U);
264 if (iter == nullptr) {
266 }
267 *out_iter = iter;
268 *out_uncomp = (uint64_t)st.m_uncomp_size;
269 return k_ra8_ok;
270}
271
294 const char* path,
295 uint64_t* out_data_ofs,
296 uint64_t* out_uncomp)
297{
298 mz_zip_archive* zip = internal_zip(book);
299 int32_t idx = 0;
300 const ra8_err_t err = internal_locate(zip, book, path, &idx);
301 if (err != k_ra8_ok) {
302 return err;
303 }
304 mz_zip_archive_file_stat st;
305 if (mz_zip_reader_file_stat(zip, (mz_uint)idx, &st) == MZ_FALSE) {
306 return k_ra8_err_validation_failed; /* GCOVR_EXCL_LINE -- stat cannot fail on a located entry */
307 }
308 const ra8_err_t gerr = priv_epub_zip_guard_entry(&st);
309 if (gerr != k_ra8_ok) {
310 return gerr; /* lying header / declared bomb: reject before the copy */
311 }
312 if (st.m_method != 0U) {
313 return k_ra8_err_not_supported; /* DEFLATE -> use the forward cursor */
314 }
315 uint8_t hdr[k_epub_ldh_size] = {};
316 if (!internal_backing_read(book, (uint64_t)st.m_local_header_ofs, hdr, sizeof(hdr))) {
317 return k_ra8_err_validation_failed; /* GCOVR_EXCL_LINE -- header bytes are within the archive */
318 }
319 if (!internal_data_offset(hdr, (uint64_t)st.m_local_header_ofs, out_data_ofs)) {
321 }
322 *out_uncomp = (uint64_t)st.m_uncomp_size;
323 return k_ra8_ok;
324}
325
326/* ---------------------------------------------------------------------------
327 * Public API.
328 * ---------------------------------------------------------------------------
329 */
330
332 const char* path,
333 epub_entry_reader_t* out_reader,
334 uint64_t* out_size)
335{
336 RA8_CHECK_NULL_PTR(book, s_tag, "book must not be nullptr");
337 RA8_CHECK_NULL_PTR(path, s_tag, "path must not be nullptr");
338 RA8_CHECK_NULL_PTR(out_reader, s_tag, "out_reader must not be nullptr");
339 (void)memset(out_reader, 0, sizeof(*out_reader));
342 }
343
344 mz_zip_reader_extract_iter_state* iter = nullptr;
345 uint64_t uncomp = 0U;
346 const ra8_err_t err = internal_open_iter(book, path, &iter, &uncomp);
347 if (err != k_ra8_ok) {
348 return err;
349 }
350 out_reader->iter = iter;
351 out_reader->book = book;
352 out_reader->total = uncomp;
353 out_reader->done = (uncomp == 0U) ? 1U : 0U;
354 if (out_size != nullptr) {
355 *out_size = uncomp;
356 }
357 return k_ra8_ok;
358}
359
360ra8_err_t epub_entry_read(epub_entry_reader_t* reader, uint8_t* buf, size_t cap, size_t* got)
361{
362 RA8_CHECK_NULL_PTR(reader, s_tag, "reader must not be nullptr");
363 RA8_CHECK_NULL_PTR(buf, s_tag, "buf must not be nullptr");
364 RA8_CHECK_NULL_PTR(got, s_tag, "got must not be nullptr");
365 *got = 0U;
366 if (reader->iter == nullptr) {
368 }
369 if (cap == 0U) {
371 }
372 if (reader->done != 0U) {
373 return k_ra8_ok; /* clean EOF already reported; *got stays 0 */
374 }
375
376 mz_zip_reader_extract_iter_state* iter = (mz_zip_reader_extract_iter_state*)reader->iter;
377 const size_t n = mz_zip_reader_extract_iter_read(iter, buf, cap);
378 reader->consumed += (uint64_t)n;
379 *got = n;
380 if (n < cap) {
381 /* A short read is end-of-entry only if every byte arrived; otherwise the
382 * compressed stream ended early / failed to inflate. */
383 if (reader->consumed >= reader->total) {
384 reader->done = 1U;
385 return k_ra8_ok;
386 }
388 }
389 return k_ra8_ok;
390}
391
393{
394 RA8_CHECK_NULL_PTR(reader, s_tag, "reader must not be nullptr");
395 if (reader->iter == nullptr) {
396 return k_ra8_ok; /* idempotent: already closed / never opened */
397 }
398 mz_zip_reader_extract_iter_state* iter = (mz_zip_reader_extract_iter_state*)reader->iter;
399 bool full = false;
400 if (reader->total > 0U) {
401 if (reader->consumed >= reader->total) {
402 full = true;
403 }
404 }
405 const mz_bool ok = mz_zip_reader_extract_iter_free(iter);
406 reader->iter = nullptr;
407 if (full) {
408 if (ok == MZ_FALSE) {
410 }
411 }
412 return k_ra8_ok;
413}
414
435 const char* path,
436 const uint8_t* buf,
437 const size_t* got)
438{
439 RA8_CHECK_NULL_PTR(book, s_tag, "book must not be nullptr");
440 RA8_CHECK_NULL_PTR(path, s_tag, "path must not be nullptr");
441 RA8_CHECK_NULL_PTR(buf, s_tag, "buf must not be nullptr");
442 RA8_CHECK_NULL_PTR(got, s_tag, "got must not be nullptr");
443 return k_ra8_ok;
444}
445
447 const char* path,
448 uint64_t offset,
449 uint8_t* buf,
450 size_t len,
451 size_t* got)
452{
453 const ra8_err_t nz = internal_pread_null_ok(book, path, buf, got);
454 if (nz != k_ra8_ok) {
455 return nz;
456 }
457 *got = 0U;
460 }
461
462 uint64_t data_ofs = 0U;
463 uint64_t uncomp = 0U;
464 const ra8_err_t err = internal_stored_data_offset(book, path, &data_ofs, &uncomp);
465 if (err != k_ra8_ok) {
466 return err;
467 }
468 if (offset >= uncomp) {
469 return k_ra8_ok; /* window starts at/after EOF; *got stays 0 */
470 }
471 const uint64_t avail = uncomp - offset;
472 const size_t n = ((uint64_t)len > avail) ? (size_t)avail : len;
473 if (!internal_backing_read(book, data_ofs + offset, buf, n)) {
474 return k_ra8_err_validation_failed; /* GCOVR_EXCL_LINE -- data window is within the archive */
475 }
476 *got = n;
477 return k_ra8_ok;
478}
EPUB (.epub) reader and chapter iterator for ra8-firmware.
@ k_epub_max_path_len
Max href length (incl.
Definition epub.h:89
bool priv_epub_book_not_ready(uint8_t in_use, uint8_t zip_archive_active)
Pure book-not-ready predicate.
void priv_epub_join_path(const char *dir, const char *name, char *dst, size_t cap)
Concatenate dir + name into dst, NUL-terminated.
static ra8_err_t internal_open_iter(epub_book_t *book, const char *path, mz_zip_reader_extract_iter_state **out_iter, uint64_t *out_uncomp)
Locate + stat an entry and start a miniz extract-iterator over it.
Definition epub_entry.c:244
static ra8_err_t internal_locate(mz_zip_archive *zip, epub_book_t *book, const char *path, int32_t *out_idx)
Locate an entry by OPF-prefixed path, falling back to the bare path.
Definition epub_entry.c:136
epub_ldh_t
Local-file-header size + the two variable-length field-length offsets.
Definition epub_entry.c:66
@ k_epub_ldh_size
Fixed local-header length, bytes.
Definition epub_entry.c:67
@ k_epub_ldh_fname_len_ofs
uint16 file-name length field offset.
Definition epub_entry.c:68
@ k_epub_ldh_extra_len_ofs
uint16 extra-field length field offset.
Definition epub_entry.c:69
epub_ldh_sig_t
Local-file-header signature bytes (little-endian 0x04034b50).
Definition epub_entry.c:76
@ k_epub_ldh_sig_0
Signature byte 0 ('P').
Definition epub_entry.c:77
@ k_epub_ldh_sig_2
Signature byte 2.
Definition epub_entry.c:79
@ k_epub_ldh_sig_1
Signature byte 1 ('K').
Definition epub_entry.c:78
@ k_epub_ldh_sig_3
Signature byte 3.
Definition epub_entry.c:80
static ra8_err_t internal_pread_null_ok(const epub_book_t *book, const char *path, const uint8_t *buf, const size_t *got)
Reject any NULL epub_entry_pread pointer argument.
Definition epub_entry.c:434
ra8_err_t epub_entry_pread(epub_book_t *book, const char *path, uint64_t offset, uint8_t *buf, size_t len, size_t *got)
Positioned read of a stored (uncompressed) archive entry – windowed random access in bounded RAM (#23...
Definition epub_entry.c:446
ra8_err_t epub_entry_read(epub_entry_reader_t *reader, uint8_t *buf, size_t cap, size_t *got)
Pull the next chunk of a streaming entry into a bounded caller buffer (#231).
Definition epub_entry.c:360
static mz_zip_archive * internal_zip(epub_book_t *book)
Borrow the book's inline mz_zip_archive.
Definition epub_entry.c:111
static ra8_err_t internal_stored_data_offset(epub_book_t *book, const char *path, uint64_t *out_data_ofs, uint64_t *out_uncomp)
Resolve a stored entry's archive data offset + uncompressed size.
Definition epub_entry.c:293
epub_le_t
Little-endian byte shift + index constants for the header fields.
Definition epub_entry.c:87
@ k_epub_le_lo
Low-order byte index.
Definition epub_entry.c:88
@ k_epub_le_shift
High-byte left shift.
Definition epub_entry.c:90
@ k_epub_le_hi
High-order byte index.
Definition epub_entry.c:89
static bool internal_data_offset(const uint8_t *hdr, uint64_t local_header_ofs, uint64_t *out_data_ofs)
Verify a 30-byte local header and return the entry's data offset.
Definition epub_entry.c:205
ra8_err_t epub_entry_close(epub_entry_reader_t *reader)
Tear down a streaming-entry cursor and release its inflate state (#231).
Definition epub_entry.c:392
static bool internal_backing_read(epub_book_t *book, uint64_t archive_ofs, uint8_t *buf, size_t n)
Read n absolute archive bytes off the book's backing (resident or streamed).
Definition epub_entry.c:170
ra8_err_t epub_entry_open(epub_book_t *book, const char *path, epub_entry_reader_t *out_reader, uint64_t *out_size)
Begin a bounded-RAM streaming extraction of one archive entry (#231).
Definition epub_entry.c:331
Iterative, bounded-RAM ZIP-entry extraction for the EPUB reader (#231).
Test-access surface for epub internal helpers (MC/DC).
ra8_err_t priv_epub_zip_guard_entry(const mz_zip_archive_file_stat *st)
Guard one ZIP entry's declared sizes against the policy.
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#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
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ 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 * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Opened EPUB book.
Definition epub.h:286
size_t zip_size
Length of the EPUB blob.
Definition epub.h:289
epub_stream_media_t stream_media
Streamed media descriptor; {} for the resident path.
Definition epub.h:318
uint8_t zip_archive_storage[k_epub_zip_archive_bytes]
Zip archive storage.
Definition epub.h:304
uint8_t zip_archive_active
1 = mz_zip_reader_init succeeded.
Definition epub.h:305
uint8_t in_use
1 = open, 0 = closed.
Definition epub.h:358
char opf_dir[k_epub_max_path_len]
Directory portion of the OPF.
Definition epub.h:345
const uint8_t * zip_bytes
Pointer to the EPUB blob.
Definition epub.h:288
Forward streaming cursor over one ZIP entry – inflate in bounded RAM (#231).
Definition epub_entry.h:93
uint64_t consumed
Bytes delivered to the caller so far.
Definition epub_entry.h:97
uint8_t done
1 once EOF has been reached / reported.
Definition epub_entry.h:98
epub_book_t * book
Owning book (archive + allocator); borrowed.
Definition epub_entry.h:95
void * iter
Opaque mz_zip_reader_extract_iter_state*; NULL when closed.
Definition epub_entry.h:94
uint64_t total
Entry uncompressed size, bytes.
Definition epub_entry.h:96
void * ctx
Opaque backing passed to read (out-lives the book).
Definition epub.h:234
epub_stream_read_fn read
Seek+read callback (non-NULL).
Definition epub.h:233