ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_cache.c
Go to the documentation of this file.
1
27
28#include "reflow_cache.h"
29
30#include <stddef.h>
31#include <stdint.h>
32
33#include "ra8_attributes.h"
34#include "ra8_err.h"
35#include "reflow.h"
36
37/* ===========================================================================
38 * Internal numeric constants (no magic numbers).
39 * ===========================================================================
40 */
41
46typedef enum : uint32_t {
47 k_priv_fnv_offset = 0x811C9DC5U,
48 k_priv_fnv_prime = 0x01000193U,
54
59typedef enum : size_t {
62
67typedef struct {
68 uint32_t magic;
69 uint32_t version;
70 uint16_t viewport_w;
71 uint16_t viewport_h;
72 uint16_t font_px;
73 uint32_t body_color;
74 uint32_t link_color;
75 uint32_t font_len;
76 uint32_t font_hash;
77 uint32_t content_len;
78 uint32_t content_hash;
79 uint32_t glyph_count;
80 uint32_t page_count;
81 uint32_t body_checksum;
83
84/* ===========================================================================
85 * Little-endian byte cursors
86 * ===========================================================================
87 */
88
107static void internal_put_u32(uint8_t* buf, size_t* off, uint32_t word)
108{
109 buf[*off] = (uint8_t)(word & (uint32_t)k_priv_byte_mask);
110 *off += 1U;
111 buf[*off] = (uint8_t)((word >> (uint32_t)k_priv_shift_8) & (uint32_t)k_priv_byte_mask);
112 *off += 1U;
113 buf[*off] = (uint8_t)((word >> (uint32_t)k_priv_shift_16) & (uint32_t)k_priv_byte_mask);
114 *off += 1U;
115 buf[*off] = (uint8_t)((word >> (uint32_t)k_priv_shift_24) & (uint32_t)k_priv_byte_mask);
116 *off += 1U;
117}
118
137static void internal_put_u16(uint8_t* buf, size_t* off, uint16_t half)
138{
139 buf[*off] = (uint8_t)(half & (uint16_t)k_priv_byte_mask);
140 *off += 1U;
141 buf[*off] = (uint8_t)((half >> (uint32_t)k_priv_shift_8) & (uint16_t)k_priv_byte_mask);
142 *off += 1U;
143}
144
165static uint32_t internal_get_u32(const uint8_t* buf, size_t* off)
166{
167 uint32_t word = (uint32_t)buf[*off];
168 *off += 1U;
169 word |= (uint32_t)buf[*off] << (uint32_t)k_priv_shift_8;
170 *off += 1U;
171 word |= (uint32_t)buf[*off] << (uint32_t)k_priv_shift_16;
172 *off += 1U;
173 word |= (uint32_t)buf[*off] << (uint32_t)k_priv_shift_24;
174 *off += 1U;
175 return word;
176}
177
198static uint16_t internal_get_u16(const uint8_t* buf, size_t* off)
199{
200 uint16_t half = (uint16_t)buf[*off];
201 *off += 1U;
202 half = (uint16_t)(half | ((uint16_t)buf[*off] << (uint32_t)k_priv_shift_8));
203 *off += 1U;
204 return half;
205}
206
207/* ===========================================================================
208 * Hash + record codecs
209 * ===========================================================================
210 */
211
233static uint32_t internal_fnv1a(const uint8_t* data, size_t len)
234{
235 uint32_t hash = (uint32_t)k_priv_fnv_offset;
236 for (size_t i = 0U; i < len; ++i) {
237 hash ^= (uint32_t)data[i];
238 hash *= (uint32_t)k_priv_fnv_prime;
239 }
240 return hash;
241}
242
263static void internal_put_glyph(uint8_t* buf, size_t* off, const reflow_glyph_t* glyph)
264{
265 internal_put_u32(buf, off, (uint32_t)glyph->x);
266 internal_put_u32(buf, off, (uint32_t)glyph->y);
267 internal_put_u32(buf, off, (uint32_t)glyph->cp);
268 internal_put_u32(buf, off, glyph->color);
269 internal_put_u16(buf, off, glyph->font_px);
270 buf[*off] = glyph->style;
271 *off += 1U;
272 buf[*off] = glyph->reserved;
273 *off += 1U;
274}
275
295static void internal_get_glyph(const uint8_t* buf, size_t* off, reflow_glyph_t* glyph)
296{
297 glyph->x = (int32_t)internal_get_u32(buf, off);
298 glyph->y = (int32_t)internal_get_u32(buf, off);
299 glyph->cp = (int32_t)internal_get_u32(buf, off);
300 glyph->color = internal_get_u32(buf, off);
301 glyph->font_px = internal_get_u16(buf, off);
302 glyph->style = buf[*off];
303 *off += 1U;
304 glyph->reserved = buf[*off];
305 *off += 1U;
306}
307
325static void internal_put_page(uint8_t* buf, size_t* off, const reflow_page_t* page)
326{
327 internal_put_u32(buf, off, page->glyph_first);
328 internal_put_u32(buf, off, page->glyph_count);
329}
330
348static void internal_get_page(const uint8_t* buf, size_t* off, reflow_page_t* page)
349{
350 page->glyph_first = internal_get_u32(buf, off);
351 page->glyph_count = internal_get_u32(buf, off);
352}
353
354/* ===========================================================================
355 * Header codecs + key compare
356 * ===========================================================================
357 */
358
379static uint32_t internal_font_hash(const reflow_t* engine)
380{
381 if (engine->font_data == nullptr) {
382 return 0U;
383 }
384 return internal_fnv1a(engine->font_data, engine->font_len);
385}
386
408static void
409internal_put_header(uint8_t* buf, const reflow_t* engine, size_t content_len, uint32_t content_hash)
410{
411 size_t off = 0U;
412 internal_put_u32(buf, &off, (uint32_t)k_reflow_cache_magic);
413 internal_put_u16(buf, &off, (uint16_t)k_reflow_cache_version);
414 internal_put_u16(buf, &off, 0U); /* reserved */
415 internal_put_u16(buf, &off, engine->viewport_w);
416 internal_put_u16(buf, &off, engine->viewport_h);
417 internal_put_u16(buf, &off, engine->font_px);
418 internal_put_u16(buf, &off, 0U); /* reserved16 */
419 internal_put_u32(buf, &off, engine->body_color);
420 internal_put_u32(buf, &off, engine->link_color);
421 internal_put_u32(buf, &off, (uint32_t)engine->font_len);
422 internal_put_u32(buf, &off, internal_font_hash(engine));
423 internal_put_u32(buf, &off, (uint32_t)content_len);
424 internal_put_u32(buf, &off, content_hash);
425 internal_put_u32(buf, &off, engine->glyph_count);
426 internal_put_u32(buf, &off, engine->page_count);
427 internal_put_u32(buf, &off, 0U); /* body_checksum placeholder */
428}
429
447static void internal_get_header(const uint8_t* buf, priv_cache_key_t* key)
448{
449 size_t off = 0U;
450 key->magic = internal_get_u32(buf, &off);
451 key->version = internal_get_u16(buf, &off);
452 (void)internal_get_u16(buf, &off); /* reserved */
453 key->viewport_w = internal_get_u16(buf, &off);
454 key->viewport_h = internal_get_u16(buf, &off);
455 key->font_px = internal_get_u16(buf, &off);
456 (void)internal_get_u16(buf, &off); /* reserved16 */
457 key->body_color = internal_get_u32(buf, &off);
458 key->link_color = internal_get_u32(buf, &off);
459 key->font_len = internal_get_u32(buf, &off);
460 key->font_hash = internal_get_u32(buf, &off);
461 key->content_len = internal_get_u32(buf, &off);
462 key->content_hash = internal_get_u32(buf, &off);
463 key->glyph_count = internal_get_u32(buf, &off);
464 key->page_count = internal_get_u32(buf, &off);
465 key->body_checksum = internal_get_u32(buf, &off);
466}
467
489static size_t internal_blob_size(uint32_t glyph_count, uint32_t page_count)
490{
491 return (size_t)k_reflow_cache_header_bytes +
492 ((size_t)glyph_count * (size_t)k_reflow_cache_glyph_bytes) +
493 ((size_t)page_count * (size_t)k_reflow_cache_page_bytes);
494}
495
520static bool internal_key_matches(const reflow_t* engine,
521 const priv_cache_key_t* key,
522 const uint8_t* content,
523 size_t content_len)
524{
525 /* One simple decision per attribute (no compound boolean): any single
526 * mismatch makes the cached layout stale. */
527 if (key->viewport_w != engine->viewport_w) {
528 return false;
529 }
530 if (key->viewport_h != engine->viewport_h) {
531 return false;
532 }
533 if (key->font_px != engine->font_px) {
534 return false;
535 }
536 if (key->body_color != engine->body_color) {
537 return false;
538 }
539 if (key->link_color != engine->link_color) {
540 return false;
541 }
542 if (key->font_len != (uint32_t)engine->font_len) {
543 return false;
544 }
545 if (key->font_hash != internal_font_hash(engine)) {
546 return false;
547 }
548 if (key->content_len != (uint32_t)content_len) {
549 return false;
550 }
551 if (key->content_hash != internal_fnv1a(content, content_len)) {
552 return false;
553 }
554 return true;
555}
556
591static ra8_err_t
592internal_parse_header(const uint8_t* buf, size_t len, priv_cache_key_t* key, size_t* need)
593{
594 if (len < (size_t)k_reflow_cache_header_bytes) {
596 }
597 internal_get_header(buf, key);
598 if (key->magic != (uint32_t)k_reflow_cache_magic) {
599 return k_ra8_err_not_found;
600 }
601 if (key->version != (uint32_t)k_reflow_cache_version) {
603 }
604 if (key->glyph_count > (uint32_t)k_reflow_max_glyphs) {
606 }
607 if (key->page_count > (uint32_t)k_reflow_max_pages) {
609 }
610 *need = internal_blob_size(key->glyph_count, key->page_count);
611 if (len < *need) {
613 }
614 return k_ra8_ok;
615}
616
617/* ===========================================================================
618 * Public API
619 * ===========================================================================
620 */
621
622[[nodiscard]] ra8_err_t reflow_cache_size(const reflow_t* engine, size_t* out_bytes)
623{
624 if ((engine == nullptr) || (out_bytes == nullptr)) {
625 return k_ra8_err_null_ptr;
626 }
627 if (engine->in_use == 0U) {
629 }
630 *out_bytes = internal_blob_size(engine->glyph_count, engine->page_count);
631 return k_ra8_ok;
632}
633
664static ra8_err_t
665internal_cache_precheck(const reflow_t* engine, const uint8_t* content, size_t content_len)
666{
667 if (engine->in_use == 0U) {
669 }
670 if ((content == nullptr) && (content_len != 0U)) {
672 }
673 if (engine->face_count != 0U) {
675 }
676 return k_ra8_ok;
677}
678
679[[nodiscard]] ra8_err_t reflow_cache_serialize(const reflow_t* engine,
680 const uint8_t* content,
681 size_t content_len,
682 uint8_t* out_buf,
683 size_t out_cap,
684 size_t* out_len)
685{
686 if ((engine == nullptr) || (out_buf == nullptr) || (out_len == nullptr)) {
687 return k_ra8_err_null_ptr;
688 }
689 const ra8_err_t verr = internal_cache_precheck(engine, content, content_len);
690 if (verr != k_ra8_ok) {
691 return verr;
692 }
693 const size_t need = internal_blob_size(engine->glyph_count, engine->page_count);
694 if (out_cap < need) {
696 }
697
698 internal_put_header(out_buf, engine, content_len, internal_fnv1a(content, content_len));
699
700 size_t off = (size_t)k_reflow_cache_header_bytes;
701 for (uint32_t i = 0U; i < engine->glyph_count; ++i) {
702 internal_put_glyph(out_buf, &off, &engine->glyphs[i]);
703 }
704 for (uint32_t i = 0U; i < engine->page_count; ++i) {
705 internal_put_page(out_buf, &off, &engine->pages[i]);
706 }
707
708 const uint32_t checksum = internal_fnv1a(&out_buf[(size_t)k_reflow_cache_header_bytes],
709 need - (size_t)k_reflow_cache_header_bytes);
710 size_t ck_off = (size_t)k_priv_checksum_off;
711 internal_put_u32(out_buf, &ck_off, checksum);
712 *out_len = need;
713 return k_ra8_ok;
714}
715
717 const uint8_t* content,
718 size_t content_len,
719 const uint8_t* buf,
720 size_t len)
721{
722 if ((engine == nullptr) || (buf == nullptr)) {
723 return k_ra8_err_null_ptr;
724 }
725 const ra8_err_t verr = internal_cache_precheck(engine, content, content_len);
726 if (verr != k_ra8_ok) {
727 return verr;
728 }
729
730 priv_cache_key_t key = {};
731 size_t need = 0U;
732 const ra8_err_t perr = internal_parse_header(buf, len, &key, &need);
733 if (perr != k_ra8_ok) {
734 return perr;
735 }
736 if (!internal_key_matches(engine, &key, content, content_len)) {
738 }
739
740 const uint32_t checksum = internal_fnv1a(&buf[(size_t)k_reflow_cache_header_bytes],
741 need - (size_t)k_reflow_cache_header_bytes);
742 if (checksum != key.body_checksum) {
744 }
745
746 size_t off = (size_t)k_reflow_cache_header_bytes;
747 for (uint32_t i = 0U; i < key.glyph_count; ++i) {
748 internal_get_glyph(buf, &off, &engine->glyphs[i]);
749 }
750 for (uint32_t i = 0U; i < key.page_count; ++i) {
751 internal_get_page(buf, &off, &engine->pages[i]);
752 }
753 engine->glyph_count = key.glyph_count;
754 engine->page_count = key.page_count;
755 engine->xhtml_buf = content;
756 engine->xhtml_len = content_len;
757 return k_ra8_ok;
758}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_checksum_mismatch
Stored / transmitted checksum does not match computed value.
Definition ra8_err.h:465
@ 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_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
static uint32_t internal_fnv1a(const void *key, uint32_t key_bytes)
FNV-1a hash of a key blob (the built-in default hash).
HTML / CSS reflow + paginate engine for the ra8d2 ereader.
ra8_err_t reflow_cache_size(const reflow_t *engine, size_t *out_bytes)
Report the exact serialised size of an engine's current layout.
static void internal_get_glyph(const uint8_t *buf, size_t *off, reflow_glyph_t *glyph)
Deserialise one glyph (20 bytes) and advance the cursor.
static void internal_get_header(const uint8_t *buf, priv_cache_key_t *key)
Parse the fixed-size header into a key struct.
static void internal_get_page(const uint8_t *buf, size_t *off, reflow_page_t *page)
Deserialise one page record (8 bytes) and advance the cursor.
static bool internal_key_matches(const reflow_t *engine, const priv_cache_key_t *key, const uint8_t *content, size_t content_len)
True when the blob's key matches the engine + supplied content.
static uint32_t internal_font_hash(const reflow_t *engine)
Hash the engine's font blob (0 when no font is bound).
ra8_err_t reflow_cache_serialize(const reflow_t *engine, const uint8_t *content, size_t content_len, uint8_t *out_buf, size_t out_cap, size_t *out_len)
Serialise the laid-out pages of engine into a keyed byte blob.
static void internal_put_u32(uint8_t *buf, size_t *off, uint32_t word)
Append a little-endian uint32_t and advance the cursor.
ra8_err_t reflow_cache_load(reflow_t *engine, const uint8_t *content, size_t content_len, const uint8_t *buf, size_t len)
Validate a cache blob and, if it matches, restore the layout.
static void internal_put_glyph(uint8_t *buf, size_t *off, const reflow_glyph_t *glyph)
Serialise one glyph (20 bytes) and advance the cursor.
static uint32_t internal_fnv1a(const uint8_t *data, size_t len)
32-bit FNV-1a hash over a byte range.
static size_t internal_blob_size(uint32_t glyph_count, uint32_t page_count)
Total serialised size for the given record counts.
static void internal_put_page(uint8_t *buf, size_t *off, const reflow_page_t *page)
Serialise one page record (8 bytes) and advance the cursor.
static uint32_t internal_get_u32(const uint8_t *buf, size_t *off)
Read a little-endian uint32_t and advance the cursor.
priv_cache_const_t
FNV-1a parameters and byte-extraction shifts / mask.
@ k_priv_shift_24
Shift to byte 3.
@ k_priv_fnv_offset
FNV-1a 32-bit offset basis.
@ k_priv_fnv_prime
FNV-1a 32-bit prime.
@ k_priv_byte_mask
Low-byte mask.
@ k_priv_shift_8
Shift to byte 1.
@ k_priv_shift_16
Shift to byte 2.
static void internal_put_header(uint8_t *buf, const reflow_t *engine, size_t content_len, uint32_t content_hash)
Write the fixed-size header (checksum left as a zero placeholder).
static ra8_err_t internal_parse_header(const uint8_t *buf, size_t len, priv_cache_key_t *key, size_t *need)
Validate a blob's header structure and report its total size.
static uint16_t internal_get_u16(const uint8_t *buf, size_t *off)
Read a little-endian uint16_t and advance the cursor.
static void internal_put_u16(uint8_t *buf, size_t *off, uint16_t half)
Append a little-endian uint16_t and advance the cursor.
priv_cache_off_t
Fixed byte offsets within the serialised header.
@ k_priv_checksum_off
Body-checksum field offset.
static ra8_err_t internal_cache_precheck(const reflow_t *engine, const uint8_t *content, size_t content_len)
Shared serialize/load precheck: init state, content args, and the #109 multi-face cache-bypass invari...
Import-time pagination cache for reflow (#79).
@ k_reflow_cache_page_bytes
Serialised bytes per page.
@ k_reflow_cache_glyph_bytes
Serialised bytes per glyph.
@ k_reflow_cache_header_bytes
Fixed header size, bytes.
@ k_reflow_cache_magic
Blob magic ('R''F''C''1').
@ k_reflow_cache_version
Serialised format version.
@ k_reflow_max_glyphs
Total positioned glyphs.
@ k_reflow_max_pages
Max paginated pages.
Parsed header / invalidation key of a cache blob.
uint16_t font_px
Body font size, pixels.
uint32_t body_checksum
FNV-1a of the record payload.
uint16_t viewport_h
Viewport height, pixels.
uint32_t glyph_count
Serialised glyph count.
uint32_t font_hash
FNV-1a of the font blob.
uint32_t version
Format version.
uint16_t viewport_w
Viewport width, pixels.
uint32_t link_color
Anchor text colour.
uint32_t body_color
Body text colour.
uint32_t content_hash
FNV-1a of the chapter bytes.
uint32_t magic
Format magic.
uint32_t font_len
Font blob length.
uint32_t content_len
Chapter byte length.
uint32_t page_count
Serialised page count.
One positioned glyph after layout.
uint8_t style
Font-style bitmask.
uint8_t reserved
Padding.
int32_t cp
Unicode code point.
uint32_t color
32-bit RGB colour.
int32_t x
Pixel column of glyph baseline-left.
uint16_t font_px
Pixel size used for this glyph.
int32_t y
Pixel row of glyph baseline.
Index range of glyphs that belong to one page.
uint32_t glyph_count
Number of glyphs in this page.
uint32_t glyph_first
Index of first glyph in this page.
Reflow / pagination engine state.
uint32_t page_count
Pages used.
const uint8_t * font_data
TTF blob; outlives the engine.
uint32_t link_color
Anchor text colour (0xRRGGBB).
reflow_glyph_t glyphs[k_reflow_max_glyphs]
Positioned glyphs.
uint32_t glyph_count
Glyphs used.
reflow_page_t pages[k_reflow_max_pages]
Page index ranges.
uint8_t in_use
1 = initialized, 0 = closed.
size_t font_len
Length of font_data, bytes.
uint16_t font_px
Body font size in pixels.
uint16_t viewport_w
Viewport width, pixels.
const uint8_t * xhtml_buf
Last layout_chapter input.
uint16_t viewport_h
Viewport height, pixels.
size_t xhtml_len
Length of xhtml_buf.
uint32_t body_color
Body text colour (0xRRGGBB).
uint8_t face_count
Embedded face count (0 = single-face).