17static_assert(
sizeof(long) <=
sizeof(int64_t),
"legacy state requires long no wider than int64");
20typedef enum : uint16_t {
30typedef enum : uint8_t {
51typedef enum : uint8_t {
77 for (
char* c = line; (*c !=
'\0') && (n < max); ++c) {
84 if ((n == max) && (
strchr(fld[max - 1U],
'\t') !=
nullptr)) {
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')) {
105 if ((digit == UINT8_MAX) || ((
int)digit >= base)) {
122 if ((text ==
nullptr) || (text[0] ==
'\0') ||
127 for (
const char* p = text; *p !=
'\0'; ++p) {
130 (value > ((UINT64_MAX - (uint64_t)digit) / (uint64_t)base))) {
133 value = (value * (uint64_t)base) + (uint64_t)digit;
148 if ((text ==
nullptr) || (text[0] ==
'\0') || (text[0] ==
'+')) {
151 const bool negative = text[0] ==
'-';
152 const char* digits = negative ? &text[1] : text;
153 if (digits[0] ==
'\0') {
156 uint64_t magnitude = 0U;
160 const uint64_t limit = negative ? ((uint64_t)INT64_MAX + 1U) : (uint64_t)INT64_MAX;
161 if (magnitude > limit) {
164 if (negative && (magnitude == limit)) {
167 *out = negative ? -(int64_t)magnitude : (int64_t)magnitude;
183 (value > (int64_t)LONG_MAX)) {
199 const char* p = *cursor;
202 if ((*p ==
'+') || (*p ==
'-')) {
206 if ((*p <
'0') || (*p >
'9')) {
209 while ((*p >=
'0') && (*p <=
'9')) {
220 *out_exp = neg ? -value : value;
247 uint64_t* out_mantissa,
248 int32_t* out_fractional,
251 const char* p = *cursor;
252 uint64_t mantissa = 0U;
253 int32_t fractional = 0;
255 bool nonzero =
false;
256 bool decimal =
false;
257 uint8_t significant = 0U;
258 while (((*p >=
'0') && (*p <=
'9')) || ((*p ==
'.') && !decimal)) {
263 nonzero = nonzero || (*p !=
'0');
264 significant += nonzero ? 1U : 0U;
269 fractional += decimal ? 1 : 0;
277 *out_mantissa = mantissa;
278 *out_fractional = fractional;
292 if ((text ==
nullptr) || (text[0] ==
'\0')) {
295 const char* p = text;
296 const bool negative = *p ==
'-';
300 uint64_t mantissa = 0U;
301 int32_t fractional = 0;
306 int32_t exponent = 0;
307 if ((*p ==
'e') || (*p ==
'E')) {
313 const int32_t scale = exponent - fractional;
328 if ((text ==
nullptr) || (
strlen(text) != 16U)) {
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) {
342 memcpy(out, &bits,
sizeof(bits));
343 return isfinite(*out);
381 if ((done > 1U) || (pages > UINT16_MAX) || (ready > UINT16_MAX) || (ready > pages) ||
382 ((done != 0U) && ((pages == 0U) || (ready != pages))) ||
555 uint64_t status = 0U;
559 (status > UINT16_MAX))) {
639 uint64_t version = 0U;
640 if ((fld[0][0] !=
'V') || (nf != 2U) ||
648 *schema_version = (uint16_t)version;
672 const char type = fld[0][0];
673 if ((fld[0][1] !=
'\0')) {
676 if (*schema_version == 0U) {
701 uint64_t direction = 0U;
810 if (eof || (
byte == (uint8_t)
'\n')) {
811 if ((used > 0U) && (line[used - 1U] ==
'\r')) {
815 *out_has_line = !eof || (used > 0U);
818 if ((used + 1U) >= capacity) {
821 line[used] = (char)
byte;
836 if ((line[0] ==
'\0') || (line[0] ==
'#')) {
849 uint16_t max_schema_version,
852 if ((storage ==
nullptr) || (file ==
nullptr) || (st ==
nullptr) ||
868 uint16_t schema_version = 0U;
869 bool has_line =
true;
870 while ((err ==
k_ra8_ok) && has_line) {
872 if ((err ==
k_ra8_ok) && has_line &&
877 if ((err ==
k_ra8_ok) && ((schema_version == 0U) || (schema_version > max_schema_version) ||
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_seek(fw_fs_file_t *file, uint64_t absolute_offset)
Seek to an absolute byte offset from the beginning.
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.
bool priv_mdl_state_valid(const mdl_state_t *st)
Validate every bound and cross-field invariant before persistence.
bool priv_mdl_state_relative_path_valid(const char *path, size_t cap)
Validate a bounded relative path.
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.
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.
mdl_chapter_rec_t * mdl_state_find_chapter(mdl_state_t *st, const char *id)
Find a chapter record by its stable identifier.
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.
mdl_state_reading_direction_t
Persisted fixed-layout reading direction.
@ k_mdl_state_read_rtl
Right-to-left page progression.
@ k_mdl_state_version
Timestamped cache schema written now.
@ k_mdl_state_version_v2
Legacy decimal schema accepted.
@ k_mdl_state_version_v3
Legacy cache-metadata schema accepted.
@ k_mdl_state_version_v1
Legacy integral schema accepted.
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.
void mdl_state_init(mdl_state_t *st)
Reset a state object to an empty, current-version library.
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_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.
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
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.
int64_t fetched_at
Completion time (epoch s).
uint16_t pages_done
Pages fetched and verified.
uint16_t page_count
Total pages known (0 = ?).
bool complete
All pages present + verified.
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).
char summary[k_mdl_summary_max]
Series synopsis.
char cover_path[k_mdl_relpath_max]
Local cover path.
char series_title[k_mdl_title_max]
Series title.
char series_url[k_mdl_url_max]
Series page URL.
char site_name[k_mdl_name_max]
Descriptor name.
mdl_state_reading_direction_t reading_direction
Page progression.
char language[k_mdl_language_max]
BCP-47 language tag.
char artist[k_mdl_person_max]
Artist/illustrator.
char site_host[k_mdl_host_max]
Site host.
char cover_url[k_mdl_url_max]
Remote cover URL.
char config_path[k_mdl_cfgpath_max]
Descriptor used.
char writer[k_mdl_person_max]
Writer/author.
One non-reentrant downloader filesystem dependency bundle.
uint8_t * io_buffer
Caller-owned stream scratch.
uint32_t io_buffer_bytes
Stream scratch extent.