ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_state_codec.c
Go to the documentation of this file.
1
8#include <limits.h>
9#include <math.h>
10#include <stdint.h>
11#include <string.h>
12
13#include "mdl_state.h"
14#include "mdl_state_internal.h"
15#include "ra8_attributes.h"
16
17static_assert(sizeof(long) <= sizeof(int64_t), "legacy state requires long no wider than int64");
18
28
49
62
63/* ---- parsing ------------------------------------------------------------- */
64
72RA8_INTERNAL static size_t internal_mdl_state_split_tabs(char* line, char* fld[], size_t max)
73{
74 size_t n = 0U;
75 fld[n] = line;
76 ++n;
77 for (char* c = line; (*c != '\0') && (n < max); ++c) {
78 if (*c == '\t') {
79 *c = '\0';
80 fld[n] = c + 1;
81 ++n;
82 }
83 }
84 if ((n == max) && (strchr(fld[max - 1U], '\t') != nullptr)) {
85 return max + 1U;
86 }
87 return n;
88}
89
97RA8_INTERNAL static bool internal_mdl_state_digit(char byte, int base, uint8_t* out)
98{
99 uint8_t digit = UINT8_MAX;
100 if ((byte >= '0') && (byte <= '9')) {
101 digit = (uint8_t)(byte - '0');
102 } else if ((byte >= 'a') && (byte <= 'f')) {
103 digit = (uint8_t)(k_state_hex_alpha_base + (byte - 'a'));
104 }
105 if ((digit == UINT8_MAX) || ((int)digit >= base)) {
106 return false;
107 }
108 *out = digit;
109 return true;
110}
111
119RA8_INTERNAL static bool
120internal_mdl_state_parse_u64_field(const char* text, int base, uint64_t* out)
121{
122 if ((text == nullptr) || (text[0] == '\0') ||
123 ((base != (int)k_state_dec_base) && (base != (int)k_state_hex_base))) {
124 return false;
125 }
126 uint64_t value = 0U;
127 for (const char* p = text; *p != '\0'; ++p) {
128 uint8_t digit = 0U;
129 if (!internal_mdl_state_digit(*p, base, &digit) ||
130 (value > ((UINT64_MAX - (uint64_t)digit) / (uint64_t)base))) {
131 return false;
132 }
133 value = (value * (uint64_t)base) + (uint64_t)digit;
134 }
135 *out = value;
136 return true;
137}
138
146RA8_INTERNAL static bool internal_mdl_state_parse_i64_field(const char* text, int64_t* out)
147{
148 if ((text == nullptr) || (text[0] == '\0') || (text[0] == '+')) {
149 return false;
150 }
151 const bool negative = text[0] == '-';
152 const char* digits = negative ? &text[1] : text;
153 if (digits[0] == '\0') {
154 return false;
155 }
156 uint64_t magnitude = 0U;
157 if (!internal_mdl_state_parse_u64_field(digits, (int)k_state_dec_base, &magnitude)) {
158 return false;
159 }
160 const uint64_t limit = negative ? ((uint64_t)INT64_MAX + 1U) : (uint64_t)INT64_MAX;
161 if (magnitude > limit) {
162 return false;
163 }
164 if (negative && (magnitude == limit)) {
165 *out = INT64_MIN;
166 } else {
167 *out = negative ? -(int64_t)magnitude : (int64_t)magnitude;
168 }
169 return true;
170}
171
179RA8_INTERNAL static bool internal_mdl_state_parse_long_field(const char* text, long* out)
180{
181 int64_t value = 0;
182 if (!internal_mdl_state_parse_i64_field(text, &value) || (value < (int64_t)LONG_MIN) ||
183 (value > (int64_t)LONG_MAX)) {
184 return false;
185 }
186 *out = (long)value;
187 return true;
188}
189
197RA8_INTERNAL static bool internal_mdl_state_parse_decimal_exp(const char** cursor, int32_t* out_exp)
198{
199 const char* p = *cursor;
200 bool neg = false;
201 int32_t value = 0;
202 if ((*p == '+') || (*p == '-')) {
203 neg = *p == '-';
204 ++p;
205 }
206 if ((*p < '0') || (*p > '9')) {
207 return false;
208 }
209 while ((*p >= '0') && (*p <= '9')) {
210 if (value > (int32_t)k_state_decimal_exp_max) {
211 return false;
212 }
213 value = (value * (int32_t)k_state_dec_base) + (int32_t)(*p - '0');
214 ++p;
215 }
216 if ((value > (int32_t)k_state_decimal_exp_max) || (*p != '\0')) {
217 return false;
218 }
219 *cursor = p;
220 *out_exp = neg ? -value : value;
221 return true;
222}
223
246RA8_INTERNAL static bool internal_mdl_state_scan_mantissa(const char** cursor,
247 uint64_t* out_mantissa,
248 int32_t* out_fractional,
249 bool* out_digit)
250{
251 const char* p = *cursor;
252 uint64_t mantissa = 0U;
253 int32_t fractional = 0;
254 bool digit = false;
255 bool nonzero = false;
256 bool decimal = false;
257 uint8_t significant = 0U;
258 while (((*p >= '0') && (*p <= '9')) || ((*p == '.') && !decimal)) {
259 if (*p == '.') {
260 decimal = true;
261 } else {
262 digit = true;
263 nonzero = nonzero || (*p != '0');
264 significant += nonzero ? 1U : 0U;
265 if (significant > (uint8_t)k_state_decimal_digits_max) {
266 return false;
267 }
268 mantissa = (mantissa * (uint64_t)k_state_dec_base) + (uint64_t)(*p - '0');
269 fractional += decimal ? 1 : 0;
270 if (fractional > (int32_t)k_state_decimal_exp_max) {
271 return false;
272 }
273 }
274 ++p;
275 }
276 *cursor = p;
277 *out_mantissa = mantissa;
278 *out_fractional = fractional;
279 *out_digit = digit;
280 return true;
281}
282
290RA8_INTERNAL static bool internal_mdl_state_parse_double_field(const char* text, double* out)
291{
292 if ((text == nullptr) || (text[0] == '\0')) {
293 return false;
294 }
295 const char* p = text;
296 const bool negative = *p == '-';
297 if (*p == '-') {
298 ++p;
299 }
300 uint64_t mantissa = 0U;
301 int32_t fractional = 0;
302 bool digit = false;
303 if (!internal_mdl_state_scan_mantissa(&p, &mantissa, &fractional, &digit)) {
304 return false;
305 }
306 int32_t exponent = 0;
307 if ((*p == 'e') || (*p == 'E')) {
308 ++p;
309 if (!internal_mdl_state_parse_decimal_exp(&p, &exponent)) {
310 return false;
311 }
312 }
313 const int32_t scale = exponent - fractional;
314 return digit && (*p == '\0') && (scale >= -(int32_t)k_state_decimal_exp_max) &&
315 (scale <= (int32_t)k_state_decimal_exp_max) &&
316 priv_mdl_state_decimal_to_binary64(mantissa, scale, negative, out);
317}
318
326RA8_INTERNAL static bool internal_mdl_state_parse_binary64(const char* text, double* out)
327{
328 if ((text == nullptr) || (strlen(text) != 16U)) {
329 return false;
330 }
331 for (size_t i = 0U; i < 16U; ++i) {
332 const bool digit = (text[i] >= '0') && (text[i] <= '9');
333 const bool lower = (text[i] >= 'a') && (text[i] <= 'f');
334 if (!digit && !lower) {
335 return false;
336 }
337 }
338 uint64_t bits = 0U;
340 return false;
341 }
342 memcpy(out, &bits, sizeof(bits));
343 return isfinite(*out);
344}
345
371 char* fld[],
372 double number,
373 bool number_known,
374 uint64_t done,
375 uint64_t pages,
376 uint64_t ready,
377 int64_t epoch,
378 size_t url_col,
379 const char* title)
380{
381 if ((done > 1U) || (pages > UINT16_MAX) || (ready > UINT16_MAX) || (ready > pages) ||
382 ((done != 0U) && ((pages == 0U) || (ready != pages))) ||
383 (mdl_state_find_chapter(st, fld[k_c_id]) != nullptr)) {
384 return false;
385 }
386 mdl_chapter_rec_t* rec =
387 mdl_state_add_chapter_numbered(st, fld[k_c_id], fld[url_col], number, number_known);
388 if ((rec == nullptr) || !mdl_state_set_chapter_metadata(rec, title, number, number_known)) {
389 return false;
390 }
391 rec->complete = done != 0U;
392 rec->page_count = (uint16_t)pages;
393 rec->pages_done = (uint16_t)ready;
394 rec->fetched_at = epoch;
395 return true;
396}
397
414RA8_INTERNAL static bool
416{
417 if (nf != (size_t)k_c_fields_v1) {
418 return false;
419 }
420 long number = 0L;
421 uint64_t done = 0U;
422 uint64_t pages = 0U;
423 uint64_t ready = 0U;
424 int64_t epoch = 0;
430 return false;
431 }
433 fld,
434 (double)number,
435 number != 0L,
436 done,
437 pages,
438 ready,
439 epoch,
440 (size_t)k_c_v1_url,
441 "");
442}
443
460RA8_INTERNAL static bool
462{
463 if (nf != (size_t)k_c_fields_v2) {
464 return false;
465 }
466 uint64_t known = 0U;
467 double number = 0.0;
468 uint64_t done = 0U;
469 uint64_t pages = 0U;
470 uint64_t ready = 0U;
471 int64_t epoch = 0;
477 !internal_mdl_state_parse_i64_field(fld[k_c_epoch], &epoch) || (known > 1U)) {
478 return false;
479 }
481 fld,
482 number,
483 known != 0U,
484 done,
485 pages,
486 ready,
487 epoch,
488 (size_t)k_c_url,
489 fld[k_c_title]);
490}
491
499RA8_INTERNAL static bool
501{
502 if (nf != (size_t)k_c_fields_v2) {
503 return false;
504 }
505 uint64_t known = 0U;
506 double number = 0.0;
507 uint64_t done = 0U;
508 uint64_t pages = 0U;
509 uint64_t ready = 0U;
510 int64_t epoch = 0;
516 !internal_mdl_state_parse_i64_field(fld[k_c_epoch], &epoch) || (known > 1U)) {
517 return false;
518 }
520 fld,
521 number,
522 known != 0U,
523 done,
524 pages,
525 ready,
526 epoch,
527 (size_t)k_c_url,
528 fld[k_c_title]);
529}
530
538RA8_INTERNAL static bool
539internal_mdl_state_apply_page(mdl_state_t* st, char* fld[], size_t nf, uint16_t schema_version)
540{
541 const bool current = schema_version == (uint16_t)k_mdl_state_version;
542 if ((current && (nf != (size_t)k_p_fields_v4)) ||
543 (!current && ((nf < (size_t)k_p_fields) || (nf > ((size_t)k_p_lastmod + 1U))))) {
544 return false;
545 }
546 uint64_t uh = 0U;
547 uint64_t ch = 0U;
550 return false;
551 }
552 const char* etag = (nf > (size_t)k_p_etag) ? fld[k_p_etag] : "";
553 const char* lastmod = (nf > (size_t)k_p_lastmod) ? fld[k_p_lastmod] : "";
554 int64_t epoch = 0;
555 uint64_t status = 0U;
556 if (current &&
559 (status > UINT16_MAX))) {
560 return false;
561 }
562 return mdl_state_add_page(st, uh, ch, fld[k_p_relpath], etag, lastmod, epoch, (uint16_t)status);
563}
564
582RA8_INTERNAL static bool internal_mdl_state_apply_kv(char* dst, size_t cap, char* fld[], size_t nf)
583{
584 if ((nf != 2U) || !priv_mdl_state_field_valid(fld[1], cap)) {
585 return false;
586 }
587 priv_mdl_state_set_opt(dst, cap, fld[1]);
588 return true;
589}
590
607RA8_INTERNAL static bool
608internal_mdl_state_apply_metadata(mdl_state_t* st, char type, char* fld[], size_t nf)
609{
610 switch (type) {
611 case 'D':
612 return internal_mdl_state_apply_kv(st->summary, sizeof(st->summary), fld, nf);
613 case 'W':
614 return internal_mdl_state_apply_kv(st->writer, sizeof(st->writer), fld, nf);
615 case 'A':
616 return internal_mdl_state_apply_kv(st->artist, sizeof(st->artist), fld, nf);
617 case 'O':
618 return internal_mdl_state_apply_kv(st->cover_url, sizeof(st->cover_url), fld, nf);
619 case 'K':
620 return (nf == 2U) && priv_mdl_state_relative_path_valid(fld[1], sizeof(st->cover_path)) &&
621 internal_mdl_state_apply_kv(st->cover_path, sizeof(st->cover_path), fld, nf);
622 case 'L':
623 return internal_mdl_state_apply_kv(st->language, sizeof(st->language), fld, nf);
624 default:
625 return false;
626 }
627}
628
636RA8_INTERNAL static bool
637internal_mdl_state_apply_version(char* fld[], size_t nf, uint16_t* schema_version)
638{
639 uint64_t version = 0U;
640 if ((fld[0][0] != 'V') || (nf != 2U) ||
641 !internal_mdl_state_parse_u64_field(fld[1], (int)k_state_dec_base, &version) ||
642 ((version != (uint64_t)k_mdl_state_version_v1) &&
643 (version != (uint64_t)k_mdl_state_version_v2) &&
644 (version != (uint64_t)k_mdl_state_version_v3) &&
645 (version != (uint64_t)k_mdl_state_version))) {
646 return false;
647 }
648 *schema_version = (uint16_t)version;
649 return true;
650}
651
669RA8_INTERNAL static bool
670internal_mdl_state_apply_line(mdl_state_t* st, char* fld[], size_t nf, uint16_t* schema_version)
671{
672 const char type = fld[0][0];
673 if ((fld[0][1] != '\0')) {
674 return false; /* a record type is exactly one character */
675 }
676 if (*schema_version == 0U) {
677 return internal_mdl_state_apply_version(fld, nf, schema_version);
678 }
679 switch (type) {
680 case 'V':
681 return false; /* duplicate headers make corruption/concatenation visible */
682 case 'S':
683 return internal_mdl_state_apply_kv(st->series_url, sizeof(st->series_url), fld, nf);
684 case 'T':
685 return internal_mdl_state_apply_kv(st->series_title, sizeof(st->series_title), fld, nf);
686 case 'N':
687 return internal_mdl_state_apply_kv(st->site_name, sizeof(st->site_name), fld, nf);
688 case 'H':
689 return internal_mdl_state_apply_kv(st->site_host, sizeof(st->site_host), fld, nf);
690 case 'G':
691 return internal_mdl_state_apply_kv(st->config_path, sizeof(st->config_path), fld, nf);
692 case 'D':
693 case 'W':
694 case 'A':
695 case 'O':
696 case 'K':
697 case 'L':
698 return (*schema_version != (uint16_t)k_mdl_state_version_v1) &&
699 internal_mdl_state_apply_metadata(st, type, fld, nf);
700 case 'R': {
701 uint64_t direction = 0U;
702 if ((*schema_version == (uint16_t)k_mdl_state_version_v1) || (nf != 2U) ||
703 !internal_mdl_state_parse_u64_field(fld[1], (int)k_state_dec_base, &direction) ||
704 (direction > (uint64_t)k_mdl_state_read_rtl)) {
705 return false;
706 }
708 return true;
709 }
710 case 'C':
711 if (*schema_version == (uint16_t)k_mdl_state_version_v1) {
712 return internal_mdl_state_apply_chapter_v1(st, fld, nf);
713 }
714 return (*schema_version == (uint16_t)k_mdl_state_version_v2)
717 case 'P':
718 return internal_mdl_state_apply_page(st, fld, nf, *schema_version);
719 default:
720 return false;
721 }
722}
723
732
741{
742 if (reader->remaining == 0U) {
743 reader->cursor = 0U;
744 reader->available = 0U;
745 return k_ra8_ok;
746 }
747 const uint32_t wanted = (reader->remaining < (uint64_t)reader->storage->io_buffer_bytes)
748 ? (uint32_t)reader->remaining
749 : reader->storage->io_buffer_bytes;
750 uint32_t count = 0U;
751 const ra8_err_t err = fw_fs_read(reader->file, reader->storage->io_buffer, wanted, &count);
752 if (err != k_ra8_ok) {
753 return err;
754 }
755 if (count == 0U) {
757 }
758 reader->remaining -= count;
759 reader->cursor = 0U;
760 reader->available = count;
761 return k_ra8_ok;
762}
763
772internal_mdl_state_reader_byte(mdl_state_reader_t* reader, uint8_t* out, bool* out_eof)
773{
774 if (reader->cursor == reader->available) {
776 if (err != k_ra8_ok) {
777 return err;
778 }
779 if (reader->available == 0U) {
780 *out_eof = true;
781 return k_ra8_ok;
782 }
783 }
784 *out = reader->storage->io_buffer[reader->cursor];
785 *out_eof = false;
786 reader->cursor += 1U;
787 return k_ra8_ok;
788}
789
798 char* line,
799 size_t capacity,
800 bool* out_has_line)
801{
802 size_t used = 0U;
803 for (;;) {
804 uint8_t byte = 0U;
805 bool eof = false;
806 ra8_err_t err = internal_mdl_state_reader_byte(reader, &byte, &eof);
807 if (err != k_ra8_ok) {
808 return err;
809 }
810 if (eof || (byte == (uint8_t)'\n')) {
811 if ((used > 0U) && (line[used - 1U] == '\r')) {
812 --used;
813 }
814 line[used] = '\0';
815 *out_has_line = !eof || (used > 0U);
816 return k_ra8_ok;
817 }
818 if ((used + 1U) >= capacity) {
820 }
821 line[used] = (char)byte;
822 ++used;
823 }
824}
825
833RA8_INTERNAL static bool
834internal_mdl_state_parse_record(mdl_state_t* st, char* line, uint16_t* schema_version)
835{
836 if ((line[0] == '\0') || (line[0] == '#')) {
837 return true;
838 }
839 char* fields[k_state_max_flds];
840 const size_t count = internal_mdl_state_split_tabs(line, fields, (size_t)k_state_max_flds);
841 return (count <= (size_t)k_state_max_flds) &&
842 internal_mdl_state_apply_line(st, fields, count, schema_version);
843}
844
846 fw_fs_file_t* file,
847 uint64_t offset,
848 uint64_t length,
849 uint16_t max_schema_version,
850 mdl_state_t* st)
851{
852 if ((storage == nullptr) || (file == nullptr) || (st == nullptr) ||
853 (storage->io_buffer == nullptr) || (storage->io_buffer_bytes == 0U) ||
854 (max_schema_version == 0U) || (max_schema_version > (uint16_t)k_mdl_state_version)) {
856 }
857 mdl_state_init(st);
858 ra8_err_t err = fw_fs_seek(file, offset);
859 if (err != k_ra8_ok) {
860 return err;
861 }
862 mdl_state_reader_t reader = {.storage = storage,
863 .file = file,
864 .remaining = length,
865 .cursor = 0U,
866 .available = 0U};
867 char line[k_mdl_state_line_max];
868 uint16_t schema_version = 0U;
869 bool has_line = true;
870 while ((err == k_ra8_ok) && has_line) {
871 err = internal_mdl_state_reader_line(&reader, line, sizeof(line), &has_line);
872 if ((err == k_ra8_ok) && has_line &&
873 !internal_mdl_state_parse_record(st, line, &schema_version)) {
875 }
876 }
877 if ((err == k_ra8_ok) && ((schema_version == 0U) || (schema_version > max_schema_version) ||
878 !priv_mdl_state_valid(st))) {
880 }
881 if (err != k_ra8_ok) {
882 mdl_state_init(st);
883 }
884 return err;
885}
886
887/* ---- coverage ------------------------------------------------------------ */
888
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.
Definition fw_if_fs.c:699
ra8_err_t fw_fs_seek(fw_fs_file_t *file, uint64_t absolute_offset)
Seek to an absolute byte offset from the beginning.
Definition fw_if_fs.c:736
void priv_mdl_state_set_opt(char *dst, size_t cap, const char *val)
Copy val into a bounded field when val is non-NULL.
Definition mdl_state.c:62
bool priv_mdl_state_valid(const mdl_state_t *st)
Validate every bound and cross-field invariant before persistence.
Definition mdl_state.c:142
bool priv_mdl_state_relative_path_valid(const char *path, size_t cap)
Validate a bounded relative path.
Definition mdl_state.c:95
bool priv_mdl_state_field_valid(const char *text, size_t cap)
True when a record field fits and cannot inject a line or column.
Definition mdl_state.c:70
Persistent per-series library state for the media downloader.
bool mdl_state_set_chapter_metadata(mdl_chapter_rec_t *chapter, const char *title, double number, bool number_known)
Set a chapter's display title and explicit parsed number.
Definition mdl_state.c:293
mdl_chapter_rec_t * mdl_state_find_chapter(mdl_state_t *st, const char *id)
Find a chapter record by its stable identifier.
Definition mdl_state.c:243
mdl_chapter_rec_t * mdl_state_add_chapter_numbered(mdl_state_t *st, const char *id, const char *url, double number, bool number_known)
Find or append a chapter with explicit parsed-number presence.
Definition mdl_state.c:262
mdl_state_reading_direction_t
Persisted fixed-layout reading direction.
Definition mdl_state.h:108
@ k_mdl_state_read_rtl
Right-to-left page progression.
Definition mdl_state.h:110
@ k_mdl_state_version
Timestamped cache schema written now.
Definition mdl_state.h:96
@ k_mdl_state_version_v2
Legacy decimal schema accepted.
Definition mdl_state.h:94
@ k_mdl_state_version_v3
Legacy cache-metadata schema accepted.
Definition mdl_state.h:95
@ k_mdl_state_version_v1
Legacy integral schema accepted.
Definition mdl_state.h:93
bool mdl_state_add_page(mdl_state_t *st, uint64_t url_hash, uint64_t content_hash, const char *rel_path, const char *etag, const char *last_modified, int64_t fetched_at, uint16_t response_status)
Add or replace a URL-keyed page cache record.
Definition mdl_state.c:347
void mdl_state_init(mdl_state_t *st)
Reset a state object to an empty, current-version library.
Definition mdl_state.c:52
mdl_state_parse_t
Parser limits and radices.
@ k_state_max_flds
Max TAB fields split from a line.
@ k_state_decimal_digits_max
Legacy writer precision bound.
@ k_state_dec_base
Radix for the decimal fields.
@ k_state_decimal_exp_max
Bounded legacy decimal scale.
@ k_state_hex_alpha_base
Value represented by ASCII a.
@ k_state_hex_base
Radix for the hex hash fields.
static bool internal_mdl_state_apply_chapter_v3(mdl_state_t *st, char *fld[], size_t nf)
Parse and apply one current-schema chapter record.
static bool internal_mdl_state_parse_long_field(const char *text, long *out)
Parse a target-width legacy signed long field exactly.
static bool internal_mdl_state_parse_binary64(const char *text, double *out)
Parse one exact-width canonical binary64 bit identity.
static bool internal_mdl_state_parse_double_field(const char *text, double *out)
Parse one complete locale-free legacy decimal binary64 value.
static bool internal_mdl_state_apply_line(mdl_state_t *st, char *fld[], size_t nf, uint16_t *schema_version)
Apply one already-split state record.
mdl_p_col_t
Field index of each column on a P (page) record line.
@ k_p_fields_v4
Exact fields a v4 P needs.
@ k_p_relpath
Path under the series.
@ k_p_etag
Cached ETag (optional).
@ k_p_epoch
Most recent fetch epoch (v4).
@ k_p_fields
Minimum legacy P fields.
@ k_p_content
Content hash (hex).
@ k_p_urlhash
Source-URL hash (hex).
@ k_p_status
Most recent HTTP status (v4).
@ k_p_lastmod
Cached Last-Modified (optional).
ra8_err_t priv_mdl_state_parse_file(mdl_storage_t *storage, fw_fs_file_t *file, uint64_t offset, uint64_t length, uint16_t max_schema_version, mdl_state_t *st)
Parse one exact state payload from an open portable file.
static bool internal_mdl_state_parse_decimal_exp(const char **cursor, int32_t *out_exp)
Parse one bounded ASCII decimal exponent.
static bool internal_mdl_state_parse_i64_field(const char *text, int64_t *out)
Parse one exact signed 64-bit decimal including INT64_MIN.
static ra8_err_t internal_mdl_state_reader_line(mdl_state_reader_t *reader, char *line, size_t capacity, bool *out_has_line)
Read one bounded text record.
static ra8_err_t internal_mdl_state_reader_byte(mdl_state_reader_t *reader, uint8_t *out, bool *out_eof)
Deliver one byte from a bounded payload reader.
mdl_c_col_t
Field index of each column on a C (chapter) record line.
@ k_c_v1_done
Complete flag (0/1).
@ k_c_url
Source URL.
@ k_c_v1_url
Source URL.
@ k_c_pages
Total page count.
@ k_c_fields_v2
Fields a v2 C line needs.
@ k_c_ready
Pages fetched + verified.
@ k_c_v1_pages
Total page count.
@ k_c_fields_v1
Fields a legacy C line needs.
@ k_c_known
Explicit parsed-number flag (v2).
@ k_c_title
Display title (v2).
@ k_c_v1_number
Integral parsed chapter number.
@ k_c_id
Chapter identifier.
@ k_c_done
Complete flag (0/1).
@ k_c_number
Parsed chapter number (v2).
@ k_c_v1_ready
Pages fetched + verified.
@ k_c_v1_epoch
Fetch time (epoch s).
@ k_c_epoch
Fetch time (epoch s).
static bool internal_mdl_state_parse_record(mdl_state_t *st, char *line, uint16_t *schema_version)
Apply one non-comment payload record.
static bool internal_mdl_state_apply_kv(char *dst, size_t cap, char *fld[], size_t nf)
Store one exact bounded key/value record.
static bool internal_mdl_state_apply_metadata(mdl_state_t *st, char type, char *fld[], size_t nf)
Apply one current-schema rich-metadata record.
static bool internal_mdl_state_apply_version(char *fld[], size_t nf, uint16_t *schema_version)
Accept one exact supported schema-version record.
static ra8_err_t internal_mdl_state_reader_refill(mdl_state_reader_t *reader)
Refill a payload reader without crossing its declared extent.
static size_t internal_mdl_state_split_tabs(char *line, char *fld[], size_t max)
Split one record in place at TAB delimiters.
static bool internal_mdl_state_apply_chapter_values(mdl_state_t *st, char *fld[], double number, bool number_known, uint64_t done, uint64_t pages, uint64_t ready, int64_t epoch, size_t url_col, const char *title)
Apply validated common chapter fields.
static bool internal_mdl_state_apply_chapter_v2(mdl_state_t *st, char *fld[], size_t nf)
Parse and apply one current v2 chapter record.
static bool internal_mdl_state_apply_chapter_v1(mdl_state_t *st, char *fld[], size_t nf)
Parse and apply one legacy v1 chapter record.
static bool internal_mdl_state_scan_mantissa(const char **cursor, uint64_t *out_mantissa, int32_t *out_fractional, bool *out_digit)
Scan a canonical decimal mantissa: digits with at most one point.
static bool internal_mdl_state_apply_page(mdl_state_t *st, char *fld[], size_t nf, uint16_t schema_version)
Parse and apply one page record.
static bool internal_mdl_state_parse_u64_field(const char *text, int base, uint64_t *out)
Parse one exact unsigned decimal or hexadecimal field.
static bool internal_mdl_state_digit(char byte, int base, uint8_t *out)
Convert one canonical lowercase ASCII digit.
bool priv_mdl_state_decimal_to_binary64(uint64_t mantissa, int32_t decimal_scale, bool negative, double *out)
Convert one exact bounded decimal rational to binary64.
Module-private validation shared by the state model and codec.
@ k_mdl_state_line_max
Serialized line cap.
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_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
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.
char * strchr(const char *s, int c)
Locate first occurrence of character in string.
Caller-owned open file; fields are private to the facade.
One chapter's coverage in library state.
Definition mdl_state.h:128
int64_t fetched_at
Completion time (epoch s).
Definition mdl_state.h:137
uint16_t pages_done
Pages fetched and verified.
Definition mdl_state.h:135
uint16_t page_count
Total pages known (0 = ?).
Definition mdl_state.h:134
bool complete
All pages present + verified.
Definition mdl_state.h:136
Bounded reader over one exact payload extent.
uint32_t available
Buffered byte count.
mdl_storage_t * storage
Caller-owned stream scratch.
uint64_t remaining
Bytes not yet delivered.
uint32_t cursor
Next buffered byte.
fw_fs_file_t * file
Open portable source file.
One series' complete persistent state (declare at file scope).
Definition mdl_state.h:175
char summary[k_mdl_summary_max]
Series synopsis.
Definition mdl_state.h:182
char cover_path[k_mdl_relpath_max]
Local cover path.
Definition mdl_state.h:186
char series_title[k_mdl_title_max]
Series title.
Definition mdl_state.h:178
char series_url[k_mdl_url_max]
Series page URL.
Definition mdl_state.h:177
char site_name[k_mdl_name_max]
Descriptor name.
Definition mdl_state.h:179
mdl_state_reading_direction_t reading_direction
Page progression.
Definition mdl_state.h:189
char language[k_mdl_language_max]
BCP-47 language tag.
Definition mdl_state.h:187
char artist[k_mdl_person_max]
Artist/illustrator.
Definition mdl_state.h:184
char site_host[k_mdl_host_max]
Site host.
Definition mdl_state.h:180
char cover_url[k_mdl_url_max]
Remote cover URL.
Definition mdl_state.h:185
char config_path[k_mdl_cfgpath_max]
Descriptor used.
Definition mdl_state.h:181
char writer[k_mdl_person_max]
Writer/author.
Definition mdl_state.h:183
One non-reentrant downloader filesystem dependency bundle.
Definition mdl_storage.h:40
uint8_t * io_buffer
Caller-owned stream scratch.
Definition mdl_storage.h:44
uint32_t io_buffer_bytes
Stream scratch extent.
Definition mdl_storage.h:47