ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
epub_open.c File Reference

epub_open() / epub_close() lifecycle plumbing. More...

#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "epub.h"
#include "epub_internal.h"
#include "epub_miniz_alloc.h"
#include "epub_xml_shim_internal.h"
#include "miniz.h"
#include "ra8_attributes.h"
#include "ra8_decomp_limits.h"
#include "ra8_err.h"
Include dependency graph for epub_open.c:

Go to the source code of this file.

Enumerations

enum  epub_internal_t : uint16_t {
  k_epub_container_xml_buf = 4096 ,
  k_epub_opf_xml_buf = 49152
}
 Implementation-only sizing constants. More...

Functions

static void internal_byte_copy (uint8_t *dst, const uint8_t *src, size_t n)
 Length-checked byte copy used in place of memcpy().
size_t priv_epub_mem_read (void *ctx, uint64_t offset, void *buf, size_t len)
 Read one bounded span from resident EPUB media.
static void internal_byte_zero (uint8_t *dst, size_t n)
 Bounded zero-fill used in place of memset(0).
void priv_epub_dirname (const char *path, char *dst, size_t cap)
 Copy the directory prefix of an EPUB package path.
static ra8_err_t internal_extract (mz_zip_archive *zip, const char *name, uint8_t *buf, size_t cap, size_t *got)
 Extract a named entry from the open zip into a stack buffer.
static void internal_zip_destroy (mz_zip_archive *zip)
 Tear down an in-place archive on the failure path.
static void internal_load_toc (mz_zip_archive *zip, epub_book_t *book, uint8_t *scratch, size_t cap)
 Best-effort: extract and parse the book's TOC document.
static ra8_err_t internal_parse_archive (mz_zip_archive *zip, epub_book_t *out_book, uint8_t *opf_scratch, size_t opf_cap)
 Run the metadata + spine parsers given an already-open zip.
ra8_err_t priv_epub_set_miniz_alloc (mz_zip_archive *zip, epub_book_t *book)
 Bind one ZIP reader to a book's caller-owned miniz arena.
size_t priv_epub_stream_read (void *opaque, mz_uint64 file_ofs, void *buf, size_t n)
 Forward one bounded miniz read to streamed EPUB media.
ra8_err_t priv_epub_finish_open (mz_zip_archive *zip, epub_book_t *out_book)
 Finish parsing one initialized ZIP reader into an EPUB book.
ra8_err_t epub_open (const void *media, const char *path, epub_book_t *out_book)
 Open an EPUB book from an opaque media handle.
ra8_err_t epub_open_streamed (const epub_stream_media_t *media, const char *path, epub_book_t *out_book)
 Open an EPUB book from a seekable stream, with no whole-file residency (#151).
ra8_err_t epub_close (epub_book_t *book)
 Close a previously opened EPUB book.

Detailed Description

epub_open() / epub_close() lifecycle plumbing.

Pulls the .epub bytes out of the opaque media handle, drives miniz to read the ZIP central directory, locates the OPF document via the bounded XML pull consumers, and populates epub_book_t.

[Ring 4 / EPUB] {World: NS}

Definition in file epub_open.c.

Enumeration Type Documentation

◆ epub_internal_t

enum epub_internal_t : uint16_t

Implementation-only sizing constants.

Enumerator
k_epub_container_xml_buf 

Stack buffer for container.xml.

k_epub_opf_xml_buf 

Static OPF + NCX/nav scratch (48 KiB; uint16_t-capped).

Real NCX TOCs exceed 16 KiB (a 77-navPoint book is ~17.6 KiB); the OPF + nav doc share this buffer.

Definition at line 40 of file epub_open.c.

Function Documentation

◆ epub_close()

ra8_err_t epub_close ( epub_book_t * book)
nodiscard

Close a previously opened EPUB book.

Parameters
[in,out]bookBook opened by epub_open().
Return values
k_ra8_okBook closed and slot released.
k_ra8_err_null_ptrbook is NULL.
k_ra8_err_not_initializedbook->in_use == 0.
Precondition
book non-NULL.
book->in_use == 1.
Postcondition
book->in_use == 0.
book->zip_archive == NULL.
Since
0.1.0

Definition at line 507 of file epub_open.c.

References epub_book_t::chapter_count, epub_miniz_arena_deinit(), epub_book_t::in_use, internal_zip_destroy(), k_ra8_err_not_initialized, k_ra8_err_null_ptr, k_ra8_ok, epub_book_t::miniz_arena, epub_book_t::zip_archive_active, and epub_book_t::zip_archive_storage.

Referenced by compile_fixture(), internal_close_compile_sources(), and rabook_import_compile_adapter().

◆ epub_open()

ra8_err_t epub_open ( const void * media,
const char * path,
epub_book_t * out_book )
nodiscard

Open an EPUB book from an opaque media handle.

Treats media as a pointer to epub_mem_media_t, opens the ZIP via miniz, follows META-INF/container.xml to the OPF document, parses metadata + manifest + spine, and populates *out_book.

Algorithm:

  1. Validate args, zero *out_book.
  2. mz_zip_reader_init_mem() against the in-memory blob.
  3. Extract META-INF/container.xml to the local stack buffer.
  4. Parse with the bounded XML reader; pull the first <rootfile> full-path.
  5. Extract the OPF file; parse the <metadata>, <manifest>, and <spine> blocks.
  6. Walk the spine in document order; for each <itemref idref="X">, look up the manifest entry with id="X" and copy its href into chapter_paths[chapter_count++].
Parameters
[in]mediaOpaque pointer; currently expected to be a epub_mem_media_t*.
[in]pathCosmetic file path for diagnostic logs; may be NULL. Not used to read bytes – the caller is responsible for loading the blob into media.
[out]out_bookPopulated book on success.
Returns
ra8_err_t
Return values
k_ra8_okBook opened.
k_ra8_err_null_ptrmedia or out_book is NULL.
k_ra8_err_invalid_argMedia payload invalid.
k_ra8_err_no_memSpine longer than k_epub_max_chapters.
k_ra8_err_validation_failedZIP/XML/OPF could not be parsed.
Precondition
media non-NULL.
out_book non-NULL.
Postcondition
On success, out_book->in_use == 1 and chapter_count >= 0.
On failure, *out_book is zero-initialized.
Note
Not thread-safe. Single-threaded init context.
See also
epub_close()
Since
0.1.0

Definition at line 405 of file epub_open.c.

References epub_mem_media_t::data, epub_miniz_arena_deinit(), internal_byte_zero(), k_ra8_err_invalid_arg, k_ra8_err_null_ptr, k_ra8_err_validation_failed, k_ra8_ok, epub_book_t::miniz_arena, priv_epub_finish_open(), priv_epub_mem_read(), priv_epub_set_miniz_alloc(), ra8_decomp_zip_entry_preflight(), epub_mem_media_t::size, epub_book_t::zip_archive_storage, epub_book_t::zip_bytes, and epub_book_t::zip_size.

Referenced by compile_fixture(), ec_render_cover_or_halt(), ep_parse_or_halt(), and est_open_or_halt().

◆ epub_open_streamed()

ra8_err_t epub_open_streamed ( const epub_stream_media_t * media,
const char * path,
epub_book_t * out_book )
nodiscard

Open an EPUB book from a seekable stream, with no whole-file residency (#151).

The streaming counterpart to epub_open(). Instead of a fully-resident blob (epub_mem_media_t), it takes a epub_stream_media_t – a seek+read callback plus the archive size – and drives miniz's user-read reader (mz_zip_reader_init) off it. Only the ZIP tail (end-of-central-directory + central directory) is read at open, and each entry (container.xml, OPF, a chapter, the cover) is inflated on demand through the same callback, so the resident working set is bounded by the largest single entry plus the central directory – never the whole archive. This is what lets a book far larger than SRAM+SDRAM (e.g. a multi-hundred-MB manga omnibus on the SD card) be opened and parsed at all.

The parsed book is identical to one opened via epub_open(): every accessor (epub_load_chapter(), epub_get_cover_image(), epub_get_resource(), ...) works unchanged, streaming each entry from the backing on demand. epub_close() tears the reader down for both paths.

Parameters
[in]mediaSeekable media descriptor; read non-NULL, size > 0. The backing referenced by media->ctx must out-live the opened book.
[in]pathCosmetic path for diagnostics; may be NULL. Not used to read bytes – all I/O goes through media->read.
[out]out_bookPopulated book on success.
Returns
ra8_err_t
Return values
k_ra8_okBook opened; streams from media on demand.
k_ra8_err_null_ptrmedia or out_book is NULL.
k_ra8_err_invalid_argmedia->read is NULL or media->size == 0.
k_ra8_err_no_memSpine longer than k_epub_max_chapters.
k_ra8_err_validation_failedZIP/XML/OPF could not be parsed / read.
Precondition
media and out_book are non-NULL.
media->read faithfully reads [offset, offset+len) of the archive.
Postcondition
On success out_book->in_use == 1 and out_book->zip_bytes == NULL.
On failure *out_book is zero-initialized.
Note
Not thread-safe. Single-threaded reader context.
The whole-file epub_open() stays the right choice for small, already resident (baked / XIP) books; this path is additive for the large/FS case.
See also
epub_open()
epub_close()
Since
0.1.0

Definition at line 455 of file epub_open.c.

References epub_stream_media_t::ctx, epub_miniz_arena_deinit(), internal_byte_zero(), k_ra8_err_invalid_arg, k_ra8_err_null_ptr, k_ra8_err_validation_failed, k_ra8_ok, epub_book_t::miniz_arena, priv_epub_finish_open(), priv_epub_set_miniz_alloc(), priv_epub_stream_read(), ra8_decomp_zip_entry_preflight(), epub_stream_media_t::read, epub_stream_media_t::size, epub_book_t::stream_media, epub_book_t::zip_archive_storage, epub_book_t::zip_bytes, and epub_book_t::zip_size.

Referenced by internal_compile_temp(), and internal_stream_open().

◆ internal_byte_copy()

void internal_byte_copy ( uint8_t * dst,
const uint8_t * src,
size_t n )
static

Length-checked byte copy used in place of memcpy().

Keeps clang-tidy's clang-analyzer-security.insecureAPI checker happy. Same effect on -O2 generated code as memcpy().

Parameters
[in]dstSee implementation.
[in]srcSee implementation.
[in]nSee implementation.
Precondition
Module state is consistent.
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 75 of file epub_open.c.

Referenced by priv_epub_dirname(), and priv_epub_mem_read().

◆ internal_byte_zero()

void internal_byte_zero ( uint8_t * dst,
size_t n )
static

Bounded zero-fill used in place of memset(0).

See implementation.

Parameters
[in]dstSee implementation.
[in]nSee implementation.
Precondition
Module state is consistent.
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 108 of file epub_open.c.

Referenced by epub_open(), and epub_open_streamed().

◆ internal_extract()

ra8_err_t internal_extract ( mz_zip_archive * zip,
const char * name,
uint8_t * buf,
size_t cap,
size_t * got )
static

Extract a named entry from the open zip into a stack buffer.

Returns the actual size in *got. k_ra8_err_no_mem if the entry is larger than cap, k_ra8_err_not_found if the entry is missing.

See implementation.

Parameters
[in]zipSee implementation.
[in]nameSee implementation.
[in]bufSee implementation.
[in]capSee implementation.
[in]gotSee implementation.
Returns
Result code.
Return values
k_ra8_okOperation succeeded.
Precondition
Module state is consistent.
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 163 of file epub_open.c.

References k_ra8_err_no_mem, k_ra8_err_not_found, k_ra8_err_validation_failed, k_ra8_ok, and priv_epub_zip_guard_entry().

Referenced by internal_load_toc(), and internal_parse_archive().

◆ internal_load_toc()

void internal_load_toc ( mz_zip_archive * zip,
epub_book_t * book,
uint8_t * scratch,
size_t cap )
static

Best-effort: extract and parse the book's TOC document.

priv_epub_xml_parse_opf() records which navigation document to use (book->toc_kind) and its href (book->toc_path). This helper joins that href onto the OPF directory, extracts the entry (falling back to the bare href for archives that store it un-prefixed), and dispatches to the NCX or nav parser. Any failure is swallowed: a book missing or with a malformed TOC is still fully readable via the spine, so this never propagates an error to epub_open().

Parameters
[in]zipOpen archive.
[in,out]bookBook whose toc table is populated.
[out]scratchScratch buffer reused for the TOC bytes.
[in]capCapacity of scratch in bytes.
Precondition
book non-NULL; zip initialised; OPF already parsed.
scratch non-NULL with cap > 0.
Postcondition
On success book->toc_count reflects the parsed entries.
On any failure book is left readable with toc_count == 0.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 248 of file epub_open.c.

References internal_extract(), k_epub_max_path_len, k_epub_toc_nav, k_epub_toc_none, k_ra8_err_not_found, k_ra8_ok, epub_book_t::opf_dir, priv_epub_join_path(), priv_epub_xml_parse_nav(), priv_epub_xml_parse_ncx(), epub_book_t::toc_kind, and epub_book_t::toc_path.

Referenced by internal_parse_archive().

◆ internal_parse_archive()

ra8_err_t internal_parse_archive ( mz_zip_archive * zip,
epub_book_t * out_book,
uint8_t * opf_scratch,
size_t opf_cap )
static

Run the metadata + spine parsers given an already-open zip.

Splits out of epub_open to keep that function under the NASA-Rule-4 statement budget enforced by clang-tidy (readability-function-size).

See implementation.

Parameters
[in]zipSee implementation.
[in]out_bookSee implementation.
[in]opf_scratchSee implementation.
[in]opf_capSee implementation.
Returns
Result code.
Return values
k_ra8_okOperation succeeded.
Precondition
Module state is consistent.
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

container.xml path inside every conformant .epub archive.

Definition at line 299 of file epub_open.c.

References internal_extract(), internal_load_toc(), k_epub_container_xml_buf, k_epub_max_path_len, k_ra8_ok, epub_book_t::opf_dir, epub_container_result_t::opf_path, priv_epub_dirname(), priv_epub_xml_parse_container(), priv_epub_xml_parse_opf(), and epub_book_t::xml_workspace.

Referenced by priv_epub_finish_open().

◆ internal_zip_destroy()

void internal_zip_destroy ( mz_zip_archive * zip)
static

Tear down an in-place archive on the failure path.

Closes miniz state but does not free the storage; the book record owns it inline.

Parameters
[in]zipSee implementation.
Precondition
Module state is consistent.
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 216 of file epub_open.c.

Referenced by epub_close(), and priv_epub_finish_open().

◆ priv_epub_dirname()

void priv_epub_dirname ( const char * path,
char * dst,
size_t cap )

Copy the directory prefix of an EPUB package path.

Test-access form of the helper used while binding the OPF base directory. Production callers keep using the public EPUB facade.

Parameters
[in]pathNUL-terminated package path.
[out]dstDestination for the directory prefix.
[in]capCapacity of dst.
Precondition
When non-null, path points to a NUL-terminated string.
A nonzero cap means dst is writable for cap bytes.
Postcondition
A destination with nonzero capacity is NUL-terminated.
A null destination or zero capacity causes no write.
Note
Test-access only and not thread-safe; performs no allocation.
MC/DC:
The destination-null / zero-capacity OR has one-invalid-at-a-time and all-valid vectors in test_epub_open.c.
Since
0.1.0

Definition at line 115 of file epub_open.c.

References internal_byte_copy(), and RA8_PRIV.

Referenced by internal_parse_archive().

◆ priv_epub_finish_open()

ra8_err_t priv_epub_finish_open ( mz_zip_archive * zip,
epub_book_t * out_book )

Finish parsing one initialized ZIP reader into an EPUB book.

Test-access form of the common resident/streamed open tail. Production callers keep using epub_open or epub_open_streamed.

Parameters
[in,out]zipInitialized miniz reader.
[in,out]out_bookZeroed destination book.
Returns
Parse result or argument-validation error.
Return values
k_ra8_okThe archive parsed and the book became live.
k_ra8_err_null_ptrOne required object was null.
k_ra8_err_decomp_entriesThe archive exceeded the entry-count policy.
k_ra8_err_decomp_output_capAn entry exceeded the output-size policy.
k_ra8_err_decomp_ratioAn entry exceeded the compression-ratio policy.
k_ra8_err_not_foundA required EPUB package entry was absent.
k_ra8_err_no_memA required entry or spine exceeded fixed storage.
k_ra8_err_invalid_sizeA required XML document was empty.
k_ra8_err_validation_failedThe archive or package metadata was invalid.
Precondition
When non-null, zip is an initialized miniz reader.
When non-null, out_book points to zeroed writable storage.
Postcondition
Success marks out_book live.
A parse failure destroys the ZIP reader without marking the book live.
Note
Test-access only; not thread-safe because parsing uses shared scratch.
MC/DC:
The ZIP-null / book-null OR has one-null-at-a-time vectors in test_epub_open.c and all-valid vectors in test_epub_open_cov.c.
Since
0.1.0

Definition at line 374 of file epub_open.c.

References epub_book_t::in_use, internal_parse_archive(), internal_zip_destroy(), k_epub_opf_xml_buf, k_ra8_err_null_ptr, k_ra8_ok, priv_epub_zip_guard_archive(), RA8_PRIV, and epub_book_t::zip_archive_active.

Referenced by epub_open(), and epub_open_streamed().

◆ priv_epub_mem_read()

size_t priv_epub_mem_read ( void * ctx,
uint64_t offset,
void * buf,
size_t len )

Read one bounded span from resident EPUB media.

Test-access form of the callback used by the resident ZIP and decompression preflight paths; production callers keep using the public EPUB facade.

Parameters
[in]ctxBound epub_mem_media_t descriptor.
[in]offsetAbsolute archive offset.
[out]bufDestination for exactly len bytes.
[in]lenRequested byte count.
Returns
Number of bytes copied, or zero when any guard rejects the request.
Return values
0One guard rejected the request.
lenExactly the requested bytes were copied.
Precondition
Non-null arguments address their documented extents.
The resident media outlives the call.
Postcondition
Success copies exactly len bytes.
Rejection does not modify the destination.
Note
Test-access only; performs no allocation.
MC/DC:
The four-condition OR rejects null media, null output, an offset past the archive, and a length beyond the remaining suffix. Its N+1 vectors are in test_epub_chapter.c.
Since
0.1.0

Definition at line 83 of file epub_open.c.

References epub_mem_media_t::data, internal_byte_copy(), RA8_PRIV, and epub_mem_media_t::size.

Referenced by epub_open().

◆ priv_epub_set_miniz_alloc()

ra8_err_t priv_epub_set_miniz_alloc ( mz_zip_archive * zip,
epub_book_t * book )

Bind one ZIP reader to a book's caller-owned miniz arena.

Test-access form of the shared resident/streamed open helper. It initializes the embedded arena and installs all three allocation callbacks plus their opaque context.

Parameters
[in,out]zipZeroed archive descriptor to configure.
[in,out]bookBook owning the arena and workspace.
Returns
Arena initialization or argument-validation status.
Return values
k_ra8_okThe arena and callbacks were installed.
k_ra8_err_null_ptrOne required object was null.
Precondition
Non-null objects are writable and distinct.
zip has not entered a miniz reader mode.
Postcondition
Success binds every allocator callback to book.
Null rejection mutates neither candidate object.
Note
Test-access only; the public EPUB ABI is unchanged.
MC/DC:
The two-condition null OR has one-null-at-a-time and all-valid vectors in test_epub_chapter.c.
Since
0.1.0

Definition at line 342 of file epub_open.c.

References epub_miniz_workspace_t::bytes, epub_miniz_alloc(), epub_miniz_arena_init(), epub_miniz_free(), epub_miniz_realloc(), k_ra8_err_null_ptr, k_ra8_ok, epub_book_t::miniz_arena, epub_book_t::miniz_workspace, and RA8_PRIV.

Referenced by epub_open(), and epub_open_streamed().

◆ priv_epub_stream_read()

size_t priv_epub_stream_read ( void * opaque,
mz_uint64 file_ofs,
void * buf,
size_t n )

Forward one bounded miniz read to streamed EPUB media.

Test-access form of the callback installed in a streamed ZIP reader. Production callers use epub_open_streamed.

Parameters
[in]opaqueBound epub_stream_media_t descriptor.
[in]file_ofsAbsolute archive offset.
[out]bufDestination for up to n bytes.
[in]nRequested byte count.
Returns
Number of bytes supplied by the media callback, or zero on rejection.
Return values
0The descriptor or callback was null, or the offset reached EOF.
nThe backing callback supplied the full bounded request.
<nThe backing callback supplied a short read.
Precondition
When non-null, opaque points to a live epub_stream_media_t.
When non-null, buf is writable for n bytes.
Postcondition
At most n bytes are written to buf.
A null descriptor or callback does not invoke backing media.
Note
Test-access only and not thread-safe; performs no allocation.
MC/DC:
The descriptor-null / callback-null OR has one-null-at-a-time and all-valid vectors in test_epub_open.c.
Since
0.1.0

Definition at line 360 of file epub_open.c.

References epub_stream_media_t::ctx, RA8_PRIV, epub_stream_media_t::read, and epub_stream_media_t::size.

Referenced by epub_open_streamed().