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

Bounded-RAM streaming + windowed extraction of a single ZIP entry (#231). More...

#include "epub_entry.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "epub.h"
#include "epub_internal.h"
#include "miniz.h"
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "ra8_err.h"
Include dependency graph for epub_entry.c:

Go to the source code of this file.

Enumerations

enum  epub_ldh_t : uint8_t {
  k_epub_ldh_size = 30U ,
  k_epub_ldh_fname_len_ofs = 26U ,
  k_epub_ldh_extra_len_ofs = 28U
}
 Local-file-header size + the two variable-length field-length offsets. More...
enum  epub_ldh_sig_t : uint8_t {
  k_epub_ldh_sig_0 = 0x50U ,
  k_epub_ldh_sig_1 = 0x4BU ,
  k_epub_ldh_sig_2 = 0x03U ,
  k_epub_ldh_sig_3 = 0x04U
}
 Local-file-header signature bytes (little-endian 0x04034b50). More...
enum  epub_le_t : uint8_t {
  k_epub_le_lo = 0U ,
  k_epub_le_hi = 1U ,
  k_epub_le_shift = 8U
}
 Little-endian byte shift + index constants for the header fields. More...

Functions

static mz_zip_archive * internal_zip (epub_book_t *book)
 Borrow the book's inline mz_zip_archive.
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.
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).
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.
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.
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.
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).
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).
ra8_err_t epub_entry_close (epub_entry_reader_t *reader)
 Tear down a streaming-entry cursor and release its inflate state (#231).
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.
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 (#231).

Variables

static const char *const s_tag = "epub_entry"
 Module log tag.

Detailed Description

Bounded-RAM streaming + windowed extraction of a single ZIP entry (#231).

The whole-entry accessors (epub_load_chapter, epub_get_resource, ...) inflate an entry via mz_zip_reader_extract_to_mem into a caller buffer sized to the entry's uncompressed size – fine for small text/CSS/font entries, but a full-page manga scan in an all-image EPUB inflates to tens of megabytes and cannot be held whole on a ~10 MB working-set device.

This TU adds two ways to read an entry without materialising it:

  • A forward streaming cursor (epub_entry_open/read/close) over miniz's mz_zip_reader_extract_iter_* iterator: the caller pulls the entry in fixed-size chunks and the resident inflate state is bounded independent of the entry's size.
  • A positioned read (epub_entry_pread) for stored (uncompressed) entries, whose bytes lie contiguously in the archive so any window can be read directly off the backing – the random-access primitive the tile source uses to page a single tile.

Both work for a resident book (epub_open) and a streamed book (epub_open_streamed); in the streamed case every compressed / stored byte is fetched on demand through the book's seek+read backing.

[Ring 4 / EPUB] {World: NS}

Definition in file epub_entry.c.

Enumeration Type Documentation

◆ epub_ldh_sig_t

enum epub_ldh_sig_t : uint8_t

Local-file-header signature bytes (little-endian 0x04034b50).

Enumerator
k_epub_ldh_sig_0 

Signature byte 0 ('P').

k_epub_ldh_sig_1 

Signature byte 1 ('K').

k_epub_ldh_sig_2 

Signature byte 2.

k_epub_ldh_sig_3 

Signature byte 3.

Definition at line 76 of file epub_entry.c.

◆ epub_ldh_t

enum epub_ldh_t : uint8_t

Local-file-header size + the two variable-length field-length offsets.

A ZIP local file header is a fixed 30-byte record followed by the file name and an extra field; the entry's stored/compressed data begins right after them. epub_entry_pread() reads the 30-byte header to learn those two lengths and thereby the data offset.

Enumerator
k_epub_ldh_size 

Fixed local-header length, bytes.

k_epub_ldh_fname_len_ofs 

uint16 file-name length field offset.

k_epub_ldh_extra_len_ofs 

uint16 extra-field length field offset.

Definition at line 66 of file epub_entry.c.

◆ epub_le_t

enum epub_le_t : uint8_t

Little-endian byte shift + index constants for the header fields.

Enumerator
k_epub_le_lo 

Low-order byte index.

k_epub_le_hi 

High-order byte index.

k_epub_le_shift 

High-byte left shift.

Definition at line 87 of file epub_entry.c.

Function Documentation

◆ epub_entry_close()

ra8_err_t epub_entry_close ( epub_entry_reader_t * reader)
nodiscard

Tear down a streaming-entry cursor and release its inflate state (#231).

Frees the miniz iterator (returning its LZ dictionary + read buffer to the allocator). If the entire entry had been read, the entry's CRC/size are verified as a corruption check; an early close (before EOF) is legal and simply releases resources. Idempotent on an already-closed cursor.

Parameters
[in,out]readerCursor from epub_entry_open().
Returns
ra8_err_t
Return values
k_ra8_okCursor closed and state released.
k_ra8_err_null_ptrreader is NULL.
k_ra8_err_validation_failedThe fully-read entry failed CRC/size verification.
Precondition
reader is a cursor (open or already closed).
Postcondition
reader->iter == NULL on return.
No inflate state remains allocated for this cursor.
Note
Not thread-safe.
See also
epub_entry_open()
Since
0.1.0

Definition at line 392 of file epub_entry.c.

References epub_entry_reader_t::consumed, epub_entry_reader_t::iter, k_ra8_err_validation_failed, k_ra8_ok, RA8_CHECK_NULL_PTR, s_tag, and epub_entry_reader_t::total.

◆ epub_entry_open()

ra8_err_t epub_entry_open ( epub_book_t * book,
const char * path,
epub_entry_reader_t * out_reader,
uint64_t * out_size )
nodiscard

Begin a bounded-RAM streaming extraction of one archive entry (#231).

Resolves path the same way epub_get_resource() does – first joined onto the OPF directory (book->opf_dir), then as a bare archive-rooted path – then starts a miniz extract-iterator over the located entry. No entry bytes are inflated yet; the caller pulls them with epub_entry_read(). The entry's uncompressed size is reported so the caller can size a progress bar or a tile grid without materialising the entry.

Parameters
[in]bookOpen book (in_use == 1, archive active).
[in]pathEntry path, OPF-dir-relative or archive-rooted, NUL-terminated.
[out]out_readerCursor to populate (zero-initialised by the caller).
[out]out_sizeReceives the entry's uncompressed size in bytes (may be NULL).
Returns
ra8_err_t
Return values
k_ra8_okCursor open; read with epub_entry_read().
k_ra8_err_null_ptrbook, path, or out_reader is NULL.
k_ra8_err_not_initializedBook not open / archive inactive.
k_ra8_err_not_foundNo entry at path (prefixed or bare).
k_ra8_err_validation_failedThe iterator could not be started (corrupt entry).
Precondition
book->in_use == 1 and the archive is active.
out_reader points at writable, zero-initialised storage.
Postcondition
On success out_reader->iter != NULL and *out_size (if given) is the size.
On any error *out_reader is zeroed and no iterator leaks.
Note
Not thread-safe; the reader serialises archive access (one open cursor at a time).
See also
epub_entry_read()
epub_entry_close()
Since
0.1.0

Definition at line 331 of file epub_entry.c.

References epub_entry_reader_t::book, epub_entry_reader_t::done, epub_book_t::in_use, internal_open_iter(), epub_entry_reader_t::iter, k_ra8_err_not_initialized, k_ra8_ok, memset(), priv_epub_book_not_ready(), RA8_CHECK_NULL_PTR, s_tag, epub_entry_reader_t::total, and epub_book_t::zip_archive_active.

◆ epub_entry_pread()

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 )
nodiscard

Positioned read of a stored (uncompressed) archive entry – windowed random access in bounded RAM (#231).

For an entry stored with no compression (ZIP method 0 – the natural choice for already-compressed pixel data or a display-native tile atlas), the uncompressed bytes lie contiguously in the archive, so any window [offset, offset+len) can be read directly off the backing without inflating from the start. This is the random-access primitive the tile source (epub_img_tiles) uses to page a single tile without holding the whole image. A short read at the entry's tail is reported via *got.

Deflated entries are rejected (k_ra8_err_not_supported): random access into a DEFLATE stream requires inflating from the start, which the forward cursor (epub_entry_read()) already provides.

Parameters
[in]bookOpen book (in_use == 1, archive active).
[in]pathEntry path, OPF-dir-relative or archive-rooted, NUL-terminated.
[in]offsetByte offset into the entry's uncompressed data.
[out]bufDestination buffer (len writable bytes).
[in]lenBytes requested.
[out]gotBytes actually read (0 at/after the entry's end).
Returns
ra8_err_t
Return values
k_ra8_okWindow read (possibly short at EOF; see *got).
k_ra8_err_null_ptrbook, path, buf, or got is NULL.
k_ra8_err_not_initializedBook not open / archive inactive.
k_ra8_err_not_foundNo entry at path (prefixed or bare).
k_ra8_err_not_supportedThe entry is DEFLATE-compressed (use the cursor).
k_ra8_err_validation_failedThe local header could not be read / is corrupt.
Precondition
book->in_use == 1 and the archive is active.
buf holds len writable bytes.
Postcondition
On success *got <= len bytes are written to buf.
On any error *got == 0.
Note
Not thread-safe.
See also
epub_entry_read()
Since
0.1.0

Definition at line 446 of file epub_entry.c.

References epub_book_t::in_use, internal_backing_read(), internal_pread_null_ok(), internal_stored_data_offset(), k_ra8_err_not_initialized, k_ra8_err_validation_failed, k_ra8_ok, priv_epub_book_not_ready(), and epub_book_t::zip_archive_active.

◆ epub_entry_read()

ra8_err_t epub_entry_read ( epub_entry_reader_t * reader,
uint8_t * buf,
size_t cap,
size_t * got )
nodiscard

Pull the next chunk of a streaming entry into a bounded caller buffer (#231).

Inflates up to cap more bytes of the entry into buf. A short read (*got < cap) means end-of-entry has been reached; a subsequent call reports *got == 0. The caller reuses the same fixed buf across calls, so the high-water resident footprint is cap – constant regardless of how large the entry inflates to.

Parameters
[in]readerCursor from epub_entry_open().
[out]bufDestination chunk buffer (cap writable bytes).
[in]capCapacity of buf, bytes (> 0).
[out]gotBytes written this call (0 at end-of-entry).
Returns
ra8_err_t
Return values
k_ra8_okChunk delivered (or clean EOF with *got == 0).
k_ra8_err_null_ptrreader, buf, or got is NULL.
k_ra8_err_not_initializedreader is closed / never opened.
k_ra8_err_invalid_sizecap == 0.
k_ra8_err_validation_failedThe compressed stream is corrupt (bad CRC / LZ).
Precondition
reader came from a successful epub_entry_open().
buf holds cap writable bytes.
Postcondition
On success reader->consumed advanced by *got.
*got == 0 iff the whole entry has now been delivered.
Note
Not thread-safe.
See also
epub_entry_open()
Since
0.1.0

Definition at line 360 of file epub_entry.c.

References epub_entry_reader_t::consumed, epub_entry_reader_t::done, epub_entry_reader_t::iter, k_ra8_err_invalid_size, k_ra8_err_not_initialized, k_ra8_err_validation_failed, k_ra8_ok, RA8_CHECK_NULL_PTR, s_tag, and epub_entry_reader_t::total.

◆ internal_backing_read()

bool internal_backing_read ( epub_book_t * book,
uint64_t archive_ofs,
uint8_t * buf,
size_t n )
static

Read n absolute archive bytes off the book's backing (resident or streamed).

See implementation. Resident books memcpy from the resident blob; a streamed book pulls the window through its seek+read callback.

Parameters
[in]bookOpen book.
[in]archive_ofsAbsolute byte offset within the archive.
[out]bufDestination (n writable bytes).
[in]nBytes to read.
Returns
true iff exactly n bytes were read.
Return values
trueExactly n bytes were copied into buf.
falseShort read, out-of-range offset, or no read backend.
Precondition
book, buf are non-NULL.
n > 0.
Postcondition
On true, buf holds the requested window.
On false, buf contents are unspecified.
Note
Not thread-safe.
Since
0.1.0

Definition at line 170 of file epub_entry.c.

References epub_stream_media_t::ctx, memcpy(), epub_stream_media_t::read, epub_book_t::stream_media, epub_book_t::zip_bytes, and epub_book_t::zip_size.

Referenced by epub_entry_pread(), and internal_stored_data_offset().

◆ internal_data_offset()

bool internal_data_offset ( const uint8_t * hdr,
uint64_t local_header_ofs,
uint64_t * out_data_ofs )
static

Verify a 30-byte local header and return the entry's data offset.

See implementation. Checks the local-header signature, then adds the fixed header size plus the file-name and extra-field lengths.

Parameters
[in]hdr30-byte local file header.
[in]local_header_ofsArchive offset the header was read from.
[out]out_data_ofsReceives the entry's stored-data archive offset.
Returns
true iff the signature is valid.
Return values
trueLocal-header signature matched; *out_data_ofs is set.
falseSignature mismatch; *out_data_ofs is left unmodified.
Precondition
hdr holds k_epub_ldh_size readable bytes.
out_data_ofs is non-NULL.
Postcondition
On true *out_data_ofs is the stored-data offset.
On false *out_data_ofs is unmodified.
Note
Pure; thread-safe.
Since
0.1.0

Definition at line 205 of file epub_entry.c.

References k_epub_ldh_extra_len_ofs, k_epub_ldh_fname_len_ofs, k_epub_ldh_sig_0, k_epub_ldh_sig_1, k_epub_ldh_sig_2, k_epub_ldh_sig_3, k_epub_ldh_size, k_epub_le_hi, k_epub_le_lo, k_epub_le_shift, and memcmp().

Referenced by internal_stored_data_offset().

◆ internal_locate()

ra8_err_t internal_locate ( mz_zip_archive * zip,
epub_book_t * book,
const char * path,
int32_t * out_idx )
static

Locate an entry by OPF-prefixed path, falling back to the bare path.

See implementation. Mirrors internal_locate_extract() in the chapter TU.

Parameters
[in]zipOpen archive.
[in]bookBook supplying opf_dir for the prefixed attempt.
[in]pathEntry path, OPF-relative or archive-rooted.
[out]out_idxReceives the located file index on success.
Returns
Result code.
Return values
k_ra8_okEntry located; *out_idx set.
k_ra8_err_not_foundNeither the prefixed nor the bare path exists.
Precondition
zip, book, path, out_idx are non-NULL.
The archive is active.
Postcondition
On success *out_idx >= 0.
On failure *out_idx is unmodified.
Note
Not thread-safe.
Since
0.1.0

Definition at line 136 of file epub_entry.c.

References k_epub_max_path_len, k_ra8_err_not_found, k_ra8_ok, epub_book_t::opf_dir, and priv_epub_join_path().

Referenced by internal_open_iter(), and internal_stored_data_offset().

◆ internal_open_iter()

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 )
static

Locate + stat an entry and start a miniz extract-iterator over it.

See implementation. Split out so epub_entry_open stays under the NASA-Rule-4 statement budget.

Parameters
[in]bookOpen book.
[in]pathEntry path (OPF-relative or archive-rooted).
[out]out_iterReceives the started iterator on success.
[out]out_uncompReceives the entry's uncompressed size.
Returns
Result code.
Return values
k_ra8_okIterator started; outputs set.
k_ra8_err_not_foundNo entry at path.
k_ra8_err_validation_failedStat / iterator start failed.
Precondition
book is open; out_iter and out_uncomp are non-NULL.
The archive is active.
Postcondition
On success *out_iter != NULL.
On failure no iterator leaks.
Note
Not thread-safe.
Since
0.1.0

Definition at line 244 of file epub_entry.c.

References internal_locate(), internal_zip(), k_ra8_err_validation_failed, k_ra8_ok, and priv_epub_zip_guard_entry().

Referenced by epub_entry_open().

◆ internal_pread_null_ok()

ra8_err_t internal_pread_null_ok ( const epub_book_t * book,
const char * path,
const uint8_t * buf,
const size_t * got )
static

Reject any NULL epub_entry_pread pointer argument.

See implementation. Split out so epub_entry_pread stays under the NASA-Rule-4 statement budget.

Parameters
[in]bookBook handle.
[in]pathEntry path.
[in]bufDestination buffer.
[in]gotOutput count.
Returns
Result code.
Return values
k_ra8_okAll four pointers are non-NULL.
k_ra8_err_null_ptrSome pointer is NULL.
Precondition
The caller forwards its own arguments.
No pointer is dereferenced here.
Postcondition
No state mutated.
Return depends solely on the inputs.
Note
Not thread-safe.
Since
0.1.0

Definition at line 434 of file epub_entry.c.

References k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by epub_entry_pread().

◆ internal_stored_data_offset()

ra8_err_t internal_stored_data_offset ( epub_book_t * book,
const char * path,
uint64_t * out_data_ofs,
uint64_t * out_uncomp )
static

Resolve a stored entry's archive data offset + uncompressed size.

See implementation. Split out so epub_entry_pread stays under the NASA-Rule-4 statement budget. Rejects DEFLATE entries.

Parameters
[in]bookOpen book.
[in]pathEntry path (OPF-relative or archive-rooted).
[out]out_data_ofsReceives the entry's stored-data archive offset.
[out]out_uncompReceives the entry's uncompressed size.
Returns
Result code.
Return values
k_ra8_okOffset + size resolved.
k_ra8_err_not_foundNo entry at path.
k_ra8_err_not_supportedThe entry is DEFLATE-compressed.
k_ra8_err_validation_failedStat / local-header read failed.
Precondition
book is open; out_data_ofs and out_uncomp are non-NULL.
The archive is active.
Postcondition
On success *out_data_ofs addresses the entry's stored bytes.
On failure the outputs are unspecified.
Note
Not thread-safe.
Since
0.1.0

Definition at line 293 of file epub_entry.c.

References internal_backing_read(), internal_data_offset(), internal_locate(), internal_zip(), k_epub_ldh_size, k_ra8_err_not_supported, k_ra8_err_validation_failed, k_ra8_ok, and priv_epub_zip_guard_entry().

Referenced by epub_entry_pread().

◆ internal_zip()

mz_zip_archive * internal_zip ( epub_book_t * book)
static

Borrow the book's inline mz_zip_archive.

See implementation.

Parameters
[in]bookOpen book owning the inline archive storage.
Returns
Pointer to the archive (never NULL for a non-NULL book).
Precondition
book is non-NULL.
book was opened (archive storage initialised).
Postcondition
No state mutated.
The returned pointer aliases book storage.
Note
Not thread-safe.
Since
0.1.0

Definition at line 111 of file epub_entry.c.

References epub_book_t::zip_archive_storage.

Referenced by internal_open_iter(), and internal_stored_data_offset().

Variable Documentation

◆ s_tag

const char* const s_tag = "epub_entry"
static

Module log tag.

Definition at line 48 of file epub_entry.c.