32 if ((bytes == 0U) || (alignment == 0U) || ((alignment & (alignment - 1U)) != 0U)) {
35 const size_t mask = alignment - 1U;
37 static_assert(
sizeof(base) >=
sizeof(workspace->
data),
"uintptr_t must preserve object pointers");
38 (void)
memcpy((
void*)&base, (
const void*)&workspace->
data,
sizeof(workspace->
data));
39 if ((workspace->
used > (
size_t)(UINTPTR_MAX - base)) ||
40 ((base + (uintptr_t)workspace->
used) > (UINTPTR_MAX - (uintptr_t)mask))) {
43 const uintptr_t cursor = base + (uintptr_t)workspace->
used;
44 const uintptr_t aligned = (cursor + (uintptr_t)mask) & ~(uintptr_t)mask;
45 const size_t start = (size_t)(aligned - base);
46 if ((start > workspace->
cap) || (bytes > (workspace->
cap - start))) {
49 workspace->
used = start + bytes;
53 return &workspace->
data[start];
64 const size_t text_len =
strlen(text);
65 const size_t suffix_len =
strlen(suffix);
66 if (suffix_len > text_len) {
69 for (
size_t i = 0U; i < suffix_len; ++i) {
70 const unsigned char left = (
unsigned char)text[text_len - suffix_len + i];
71 const unsigned char right = (
unsigned char)suffix[i];
72 if (tolower(left) != tolower(right)) {
88 if ((name ==
nullptr) || (name[0] ==
'\0') || (name[0] ==
'/') || (name[0] ==
'\\')) {
91 const char* segment = name;
92 for (
const char* cursor = name;; ++cursor) {
93 if ((*cursor ==
'\\') || (*cursor ==
'/') || (*cursor ==
'\0')) {
94 const size_t bytes = (size_t)(cursor - segment);
95 if ((*cursor ==
'\\') || (bytes == 0U) || ((bytes == 1U) && (segment[0] ==
'.')) ||
96 ((bytes == 2U) && (segment[0] ==
'.') && (segment[1] ==
'.'))) {
99 if (*cursor ==
'\0') {
102 segment = cursor + 1;
109 if ((path ==
nullptr) || (out_format ==
nullptr)) {
112 static const struct {
121 for (
size_t i = 0U; i < (
sizeof(formats) /
sizeof(formats[0])); ++i) {
123 *out_format = formats[i].format;
141 if ((items != 0U) && (size > (SIZE_MAX / items))) {
145 const size_t bytes = items * size;
152 sizeof(*header) + bytes,
153 _Alignof(max_align_t));
154 if (header ==
nullptr) {
158 header->
bytes = bytes;
159 return (
void*)(header + 1);
172 if (address ==
nullptr) {
175 if ((items != 0U) && (size > (SIZE_MAX / items))) {
181 if (next !=
nullptr) {
182 const size_t next_bytes = items * size;
183 memcpy(next, address, previous->
bytes < next_bytes ? previous->
bytes : next_bytes);
198 io->file = &io->owned_file;
230 return (prior ==
k_ra8_ok) ? close_error : prior;
234 uint8_t* destination,
239 while (total < length) {
240 const size_t remaining = length - total;
241 const uint32_t chunk = (remaining > UINT32_MAX) ? UINT32_MAX : (uint32_t)remaining;
267 if ((io->read_error !=
k_ra8_ok) || (offset > io->size_bytes)) {
270 const uint64_t available = io->
size_bytes - offset;
271 if ((uint64_t)length > available) {
272 length = (size_t)available;
279 if ((error ==
k_ra8_ok) && (got != length)) {
283 io->read_error = error;
323 mz_zip_archive_file_stat member;
324 if (mz_zip_reader_file_stat(zip, index, &member) == MZ_FALSE) {
330 if (!member.m_is_directory &&
335 const char* name = member.m_filename;
337 scan->comicinfo = scan->comicinfo ||
internal_ends_ci(name,
"ComicInfo.xml");
338 scan->mimetype = scan->mimetype || (
strcmp(name,
"mimetype") == 0);
339 scan->container = scan->container || (
strcmp(name,
"META-INF/container.xml") == 0);
354 if ((scan->members == 0U) || (scan->pages == 0U)) {
361 !(scan->mimetype && scan->container && scan->opf && scan->nav)) {
364 report->page_count = scan->pages;
365 report->member_count = scan->members;
366 report->metadata_present = (format ==
k_mdl_format_cbz) ? scan->comicinfo : scan->opf;
382 mz_zip_archive zip = {};
386 zip.m_pAlloc_opaque = &arena;
388 zip.m_pIO_opaque = io;
390 if ((io->size_bytes == 0U) || (mz_zip_reader_init(&zip, io->size_bytes, 0) == MZ_FALSE)) {
393 const mz_uint total = mz_zip_reader_get_num_files(&zip);
396 for (mz_uint i = 0U; (error ==
k_ra8_ok) && (i < total); ++i) {
399 if ((error ==
k_ra8_ok) && (mz_zip_reader_end(&zip) == MZ_FALSE)) {
409 error = io->read_error;
425 if (offset > io->size_bytes) {
428 const uint64_t available = io->
size_bytes - offset;
429 if ((uint64_t)length > available) {
430 length = (size_t)available;
445 if (io->size_bytes == 0U) {
451 report->page_count = 1U;
499 (file ==
nullptr) || (workspace ==
nullptr) || (workspace->
data ==
nullptr) ||
500 (report ==
nullptr)) {
503 workspace->
used = 0U;
508 .size_bytes = size_bytes,
526 if ((storage ==
nullptr) || (storage->
fs ==
nullptr) || (storage->
file_workspace ==
nullptr) ||
528 (storage->
io_buffer_bytes == 0U) || (path ==
nullptr) || (workspace ==
nullptr) ||
529 (workspace->
data ==
nullptr) || (report ==
nullptr)) {
532 workspace->
used = 0U;
ra8_err_t fw_fs_open(const fw_fs_stream_port_t *streams, const char *path, fw_fs_open_mode_t mode, fw_fs_file_t *file, void *workspace, uint32_t workspace_size)
Open a file into a caller-owned handle and backend workspace.
ra8_err_t fw_fs_read(fw_fs_file_t *file, uint8_t *dst, uint32_t cap, uint32_t *out_read)
Read up to cap bytes; zero bytes is EOF.
ra8_err_t fw_fs_close(fw_fs_file_t *file)
Close and consume an open handle.
ra8_err_t fw_fs_seek(fw_fs_file_t *file, uint64_t absolute_offset)
Seek to an absolute byte offset from the beginning.
ra8_err_t fw_fs_file_size(fw_fs_file_t *file, uint64_t *out_size)
Report the open file's current length.
@ k_fw_fs_open_read
Existing file, read-only.
JOF band-tile atlas: the display-native normalized image format (#231, shared with the longstrip scro...
ra8_err_t jof_parse(jof_pread_fn pread, void *pread_ctx, uint64_t total_size, jof_info_t *out_info)
Parse + validate a JOF atlas's header, footer and index bounds.
struct mdl_export_workspace mdl_export_workspace_t
Caller-owned bounded arena for all exporter scratch state.
static bool internal_ends_ci(const char *text, const char *suffix)
Test a suffix without case sensitivity.
ra8_err_t mdl_verify_open_file(mdl_storage_t *storage, mdl_format_t format, fw_fs_file_t *file, uint64_t size_bytes, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Validate an artifact through a borrowed open filesystem handle.
void priv_mdl_verify_arena_free(void *opaque, void *address)
Accept a miniz free for a monotonic arena.
ra8_err_t mdl_verify_file(mdl_storage_t *storage, mdl_format_t format, const char *path, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Validate a completed artifact using caller-owned scratch only.
bool mdl_format_is_verifiable(mdl_format_t format)
Report whether a format has an in-process structural validator.
static ra8_err_t internal_io_close(mdl_verify_io_t *io, ra8_err_t prior)
Close an input without masking prior failure.
static ra8_err_t internal_io_open(mdl_storage_t *storage, const char *path, mdl_verify_io_t *io)
Open a portable read-only input.
ra8_err_t mdl_format_from_path(const char *path, mdl_format_t *out_format)
Infer an artifact format from its complete path suffix.
static ra8_err_t internal_zip_member(mz_zip_archive *zip, mz_uint index, mdl_zip_scan_t *scan)
Validate one ZIP member.
static ra8_err_t internal_verify_borrowed(mdl_verify_io_t *io, mdl_format_t format, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Dispatch validation over one borrowed input.
ra8_err_t priv_mdl_verify_io_read_up_to(mdl_verify_io_t *io, uint8_t *destination, size_t length, size_t *out_read)
Read up to a requested portable span.
static void * internal_arena_realloc(void *opaque, void *address, size_t items, size_t size)
Grow a miniz span by copying it to the next arena allocation.
static ra8_err_t internal_verify_zip(mdl_verify_io_t *io, mdl_format_t format, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Validate a ZIP-backed artifact.
bool priv_mdl_verify_safe_member_name(const char *name)
Reject unsafe archive member paths.
static size_t internal_zip_read(void *opaque, mz_uint64 offset, void *destination, size_t length)
Adapt portable reads to miniz.
static ra8_err_t internal_verify_jof(mdl_verify_io_t *io, mdl_verify_report_t *report)
Validate a JOF artifact.
static ra8_err_t internal_jof_pread(void *opaque, uint64_t offset, uint8_t *destination, size_t length, size_t *got)
Adapt JOF positioned reads.
void * priv_mdl_verify_workspace_take(mdl_export_workspace_t *workspace, size_t bytes, size_t alignment)
Reserve one aligned span from the verifier's caller-owned arena.
void * priv_mdl_verify_arena_alloc(void *opaque, size_t items, size_t size)
Allocate one aligned miniz span from a monotonic arena.
static size_t internal_discard_zip(void *opaque, mz_uint64 offset, const void *data, size_t bytes)
Discard verified ZIP output.
static ra8_err_t internal_zip_semantics(mdl_format_t format, const mdl_zip_scan_t *scan, mdl_verify_report_t *report)
Enforce ZIP format semantics.
bool priv_mdl_verify_is_image(const char *name)
Recognize supported image suffixes.
Bounded, no-heap structural validation of mdl artifacts.
Private bounded validator seams shared by the mdl verifiers.
ra8_err_t priv_mdl_verify_tar(mdl_verify_io_t *io, mdl_verify_report_t *report)
Validate an uncompressed CBT.
@ k_verify_member_max
Maximum archive member count.
ra8_err_t priv_mdl_verify_gzip_tar(mdl_verify_io_t *io, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Validate a gzip-compressed CBT.
ra8_err_t priv_mdl_verify_rabook(fw_fs_file_t *file, uint64_t size_bytes, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Strictly validate one borrowed RBKC .rabook file.
Private strict RBKC validator seam for media downloader artifacts.
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_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
@ k_ra8_err_invalid_arg
Invalid function argument.
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
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.
Caller-owned open file; fields are private to the facade.
fw_fs_stream_port_t streams
Byte-stream operations.
Parsed + validated geometry of one JOF atlas.
uint32_t tile_count
Total tiles (== cols * rows).
size_t high_water
Largest used value observed.
size_t used
Current allocation high edge.
size_t cap
Total arena capacity.
uint8_t * data
Writable arena bytes.
One non-reentrant downloader filesystem dependency bundle.
uint32_t file_workspace_bytes
File workspace extent.
uint8_t * io_buffer
Caller-owned stream scratch.
const fw_fs_t * fs
Injected portable filesystem.
void * file_workspace
Open-file backend state.
uint32_t io_buffer_bytes
Stream scratch extent.
Allocation bridge retaining an explicit capacity failure.
bool exhausted
An allocation did not fit.
mdl_export_workspace_t * workspace
Caller-owned bump arena.
One open portable input and its immutable size snapshot.
uint64_t size_bytes
Length observed after open.
Semantic markers accumulated while scanning ZIP members.
bool mimetype
EPUB mimetype found.
size_t pages
Image member count.
bool opf
EPUB package document found.
bool nav
EPUB navigation found.
bool comicinfo
CBZ ComicInfo.xml found.
bool container
EPUB container.xml found.
size_t members
Total member count.