ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_tokenize_lex.c
Go to the documentation of this file.
1
26
27#include <stddef.h>
28#include <stdint.h>
29#include <string.h>
30
31#include "ra8_attributes.h"
32#include "ra8_err.h"
33#include "reflow.h"
35
36/* ===========================================================================
37 * Public (test-exposed) helpers -- documented in reflow_tokenize_internal.h
38 * ===========================================================================
39 */
40
42{
43 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f') || (c == '\v');
44}
45
46size_t priv_reflow_tok_utf8_encode(uint32_t cp, uint8_t* dst)
47{
48 uint32_t value = cp;
49 if (value > (uint32_t)k_priv_uc_max) {
50 value = (uint32_t)k_priv_uc_max;
51 }
52 if (value < (uint32_t)k_priv_uc_2byte) {
53 dst[0] = (uint8_t)value;
54 return 1U;
55 }
56 if (value < (uint32_t)k_priv_uc_3byte) {
57 dst[0] = (uint8_t)((uint32_t)k_priv_utf8_lead2 | (value >> (uint32_t)k_priv_utf8_sh6));
58 dst[1] = (uint8_t)((uint32_t)k_priv_utf8_cont | (value & (uint32_t)k_priv_utf8_mask));
59 return 2U;
60 }
61 if (value < (uint32_t)k_priv_uc_4byte) {
62 dst[0] = (uint8_t)((uint32_t)k_priv_utf8_lead3 | (value >> (uint32_t)k_priv_utf8_sh12));
63 dst[1] = (uint8_t)((uint32_t)k_priv_utf8_cont |
64 ((value >> (uint32_t)k_priv_utf8_sh6) & (uint32_t)k_priv_utf8_mask));
65 dst[2] = (uint8_t)((uint32_t)k_priv_utf8_cont | (value & (uint32_t)k_priv_utf8_mask));
66 return 3U;
67 }
68 dst[0] = (uint8_t)((uint32_t)k_priv_utf8_lead4 | (value >> (uint32_t)k_priv_utf8_sh18));
69 dst[1] = (uint8_t)((uint32_t)k_priv_utf8_cont |
70 ((value >> (uint32_t)k_priv_utf8_sh12) & (uint32_t)k_priv_utf8_mask));
71 dst[2] = (uint8_t)((uint32_t)k_priv_utf8_cont |
72 ((value >> (uint32_t)k_priv_utf8_sh6) & (uint32_t)k_priv_utf8_mask));
73 dst[3] = (uint8_t)((uint32_t)k_priv_utf8_cont | (value & (uint32_t)k_priv_utf8_mask));
74 return 4U;
75}
76
97static size_t internal_local_lower(const char* name, size_t len, char* dst)
98{
99 size_t start = 0U;
100 for (size_t k = 0U; k < len; ++k) {
101 if (name[k] == ':') {
102 start = k + 1U;
103 }
104 }
105 size_t n = 0U;
106 for (size_t k = start; (k < len) && ((n + 1U) < (size_t)k_priv_tag_name_cap); ++k) {
107 char c = name[k];
108 if ((c >= 'A') && (c <= 'Z')) {
109 c = (char)(c + ('a' - 'A'));
110 }
111 dst[n] = c;
112 ++n;
113 }
114 dst[n] = '\0';
115 return n;
116}
117
118reflow_html_tag_t priv_reflow_tok_classify(const char* name, size_t len)
119{
120 if (name == nullptr) {
122 }
123 char lower[k_priv_tag_name_cap];
124 (void)internal_local_lower(name, len, lower);
125
126 static const struct {
127 const char* word;
129 } k_map[] = {
130 {"p", k_reflow_tag_p},
131 {"h1", k_reflow_tag_h1},
132 {"h2", k_reflow_tag_h2},
133 {"h3", k_reflow_tag_h3},
134 {"h4", k_reflow_tag_h4},
135 {"h5", k_reflow_tag_h5},
136 {"h6", k_reflow_tag_h6},
137 {"em", k_reflow_tag_em},
138 {"strong", k_reflow_tag_strong},
139 {"b", k_reflow_tag_b},
140 {"i", k_reflow_tag_i},
141 {"br", k_reflow_tag_br},
142 {"hr", k_reflow_tag_hr},
143 {"ul", k_reflow_tag_ul},
144 {"ol", k_reflow_tag_ol},
145 {"li", k_reflow_tag_li},
146 {"blockquote", k_reflow_tag_blockquote},
147 {"a", k_reflow_tag_a},
148 {"img", k_reflow_tag_img},
149 {"link", k_reflow_tag_link},
150 {"table", k_reflow_tag_table},
151 {"tr", k_reflow_tag_tr},
152 {"td", k_reflow_tag_td},
153 {"th", k_reflow_tag_th},
154 };
155 for (size_t k = 0U; k < (sizeof(k_map) / sizeof(k_map[0])); ++k) {
156 if (strcmp(lower, k_map[k].word) == 0) {
157 return k_map[k].tag;
158 }
159 }
161}
162
183static bool
184internal_decode_numeric(const char* src, size_t avail, uint32_t* out_cp, size_t* out_used)
185{
186 size_t i = 2U; /* past "&#" */
187 uint32_t base = (uint32_t)k_priv_base_dec;
188 /* mcdc-deactivated: the sole caller priv_reflow_tok_decode_entity guarantees window >= k_priv_entity_min (4) before delegating, so i == 2 < avail always holds; the (i < avail) bound cannot be flipped false on any public path. */
189 if ((i < avail) && ((src[i] == 'x') || (src[i] == 'X'))) {
190 base = (uint32_t)k_priv_base_hex;
191 ++i;
192 }
193 uint32_t cp = 0U;
194 size_t digits = 0U;
195 while ((i < avail) && (src[i] != ';')) {
196 char c = src[i];
197 uint32_t d; /* assigned on every non-returning path below */
198 if ((c >= '0') && (c <= '9')) {
199 d = (uint32_t)(uint8_t)c - (uint32_t)(uint8_t)'0';
200 } else if ((base == (uint32_t)k_priv_base_hex) && (c >= 'a') && (c <= 'f')) {
201 d = ((uint32_t)(uint8_t)c - (uint32_t)(uint8_t)'a') + (uint32_t)k_priv_hex_offset;
202 } else if ((base == (uint32_t)k_priv_base_hex) && (c >= 'A') && (c <= 'F')) {
203 d = ((uint32_t)(uint8_t)c - (uint32_t)(uint8_t)'A') + (uint32_t)k_priv_hex_offset;
204 } else {
205 return false;
206 }
207 cp = (cp * base) + d;
208 ++digits;
209 ++i;
210 }
211 /* mcdc-deactivated: the scan loop above exits with i < avail only when src[i] == ';', so (src[i] != ';') is co-determined by (i >= avail) and can never independently flip; its independence pair is structurally unreachable. */
212 if ((digits == 0U) || (i >= avail) || (src[i] != ';')) {
213 return false;
214 }
215 *out_cp = cp;
216 *out_used = i + 1U;
217 return true;
218}
219
220bool priv_reflow_tok_decode_entity(const char* src,
221 size_t avail,
222 uint32_t* out_cp,
223 size_t* out_used)
224{
225 const size_t window =
226 (avail < (size_t)k_priv_entity_window) ? avail : (size_t)k_priv_entity_window;
227 if ((window < (size_t)k_priv_entity_min) || (src[1] == '\0')) {
228 return false;
229 }
230 if (src[1] == '#') {
231 return internal_decode_numeric(src, window, out_cp, out_used);
232 }
233 static const struct {
234 const char* word;
235 uint32_t cp;
236 } k_named[] = {
237 {"amp", (uint32_t)'&'},
238 {"lt", (uint32_t)'<'},
239 {"gt", (uint32_t)'>'},
240 {"quot", (uint32_t)'"'},
241 {"apos", (uint32_t)'\''},
242 };
243 for (size_t e = 0U; e < (sizeof(k_named) / sizeof(k_named[0])); ++e) {
244 const size_t wlen = strlen(k_named[e].word);
245 if (((wlen + 2U) <= window) && (src[1U + wlen] == ';') &&
246 (strncmp(&src[1], k_named[e].word, wlen) == 0)) {
247 *out_cp = k_named[e].cp;
248 *out_used = wlen + 2U;
249 return true;
250 }
251 }
252 return false;
253}
254
255/* ===========================================================================
256 * Block / inline-style classification
257 * ===========================================================================
258 */
259
277{
278 switch (tag) {
279 case k_reflow_tag_p:
280 case k_reflow_tag_h1:
281 case k_reflow_tag_h2:
282 case k_reflow_tag_h3:
283 case k_reflow_tag_h4:
284 case k_reflow_tag_h5:
285 case k_reflow_tag_h6:
286 case k_reflow_tag_ul:
287 case k_reflow_tag_ol:
288 case k_reflow_tag_li:
291 case k_reflow_tag_tr:
292 case k_reflow_tag_td:
293 case k_reflow_tag_th:
294 return true;
295 default:
296 return false;
297 }
298}
299
317{
318 switch (tag) {
319 case k_reflow_tag_b:
321 return (uint8_t)k_reflow_style_bold;
322 case k_reflow_tag_i:
323 case k_reflow_tag_em:
324 return (uint8_t)k_reflow_style_italic;
325 case k_reflow_tag_a:
326 return (uint8_t)k_reflow_style_underline;
327 default:
328 return 0U;
329 }
330}
331
332/* ===========================================================================
333 * Token-pool emit + text-pool feed / stash + literal scanners
334 * ===========================================================================
335 */
336
361 uint8_t style,
362 uint32_t off,
363 uint32_t len)
364{
365 if (engine->token_count >= (uint32_t)k_reflow_max_tokens) {
366 return false;
367 }
368 reflow_token_t* tok = &engine->tokens[engine->token_count];
369 tok->kind = (uint8_t)kind;
370 tok->tag = (uint8_t)tag;
371 tok->style = style;
372 tok->reserved = 0U;
373 tok->text_off = off;
374 tok->text_len = len;
375 tok->color = (uint32_t)k_reflow_color_inherit;
376 tok->css_font_px = 0U;
377 tok->reserved16 = 0U;
378 engine->token_count++;
379 return true;
380}
381
400bool priv_reflow_tok_feed(reflow_t* engine, char ch, bool* last_ws)
401{
402 char value = ch;
404 if (*last_ws) {
405 return true;
406 }
407 value = ' ';
408 *last_ws = true;
409 } else {
410 *last_ws = false;
411 }
412 if (engine->text_pool_used >= (uint32_t)k_reflow_text_pool_bytes) {
413 return false;
414 }
415 engine->text_pool[engine->text_pool_used] = (uint8_t)value;
416 engine->text_pool_used++;
417 return true;
418}
419
438static bool internal_feed_utf8(reflow_t* engine, uint32_t cp, bool* last_ws)
439{
440 uint8_t enc[4];
441 const size_t n = priv_reflow_tok_utf8_encode(cp, enc);
442 for (size_t b = 0U; b < n; ++b) {
443 if (!priv_reflow_tok_feed(engine, (char)enc[b], last_ws)) {
444 return false;
445 }
446 }
447 return true;
448}
449
473static bool internal_stash_one(reflow_t* engine,
474 const uint8_t* buf,
475 size_t i,
476 size_t end,
477 bool* last_ws,
478 size_t* consumed)
479{
480 if (buf[i] == (uint8_t)'&') {
481 uint32_t cp = 0U;
482 size_t used = 0U;
483 if (priv_reflow_tok_decode_entity((const char*)&buf[i], end - i, &cp, &used)) {
484 *consumed = used;
485 return internal_feed_utf8(engine, cp, last_ws);
486 }
487 }
488 *consumed = 1U;
489 return priv_reflow_tok_feed(engine, (char)buf[i], last_ws);
490}
491
515 const uint8_t* buf,
516 size_t start,
517 size_t end,
518 uint32_t* out_off,
519 uint32_t* out_len)
520{
521 *out_off = engine->text_pool_used;
522 bool last_ws = true; /* drop leading whitespace */
523 size_t i = start;
524 while (i < end) {
525 size_t consumed = 0U;
526 if (!internal_stash_one(engine, buf, i, end, &last_ws, &consumed)) {
527 return false;
528 }
529 i += consumed;
530 }
531 *out_len = engine->text_pool_used - *out_off;
532 return true;
533}
534
555static bool internal_bytes_equal(const uint8_t* buf, const char* lit, size_t len)
556{
557 bool equal = true;
558 for (size_t k = 0U; (k < len) && equal; ++k) {
559 equal = buf[k] == (uint8_t)lit[k];
560 }
561 return equal;
562}
563
564bool priv_reflow_tok_starts_with(const uint8_t* buf, size_t i, size_t len, const char* lit)
565{
566 const size_t n = strlen(lit);
567 if ((i + n) > len) {
568 return false;
569 }
570 return internal_bytes_equal(&buf[i], lit, n);
571}
572
591size_t priv_reflow_tok_skip_past(const uint8_t* buf, size_t i, size_t len, const char* lit)
592{
593 const size_t n = strlen(lit);
594 size_t pos = i;
595 while ((pos + n) <= len) {
596 if (internal_bytes_equal(&buf[pos], lit, n)) {
597 return pos + n;
598 }
599 ++pos;
600 }
601 return len;
602}
603
623size_t priv_reflow_tok_find_lit(const uint8_t* buf, size_t i, size_t len, const char* lit)
624{
625 const size_t n = strlen(lit);
626 size_t pos = i;
627 while ((pos + n) <= len) {
628 if (internal_bytes_equal(&buf[pos], lit, n)) {
629 return pos;
630 }
631 ++pos;
632 }
633 return len;
634}
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.
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
size_t strlen(const char *s)
Calculate string length.
HTML / CSS reflow + paginate engine for the ra8d2 ereader.
Test-access surface for reflow_tokenize.c internal helpers.
@ k_priv_utf8_lead4
4-byte sequence lead-byte prefix.
@ k_priv_entity_window
Max bytes scanned for one '&...;'.
@ k_priv_hex_offset
Value of hex 'a'/'A' minus the letter.
@ k_priv_utf8_sh18
Shift for three continuation bytes.
@ k_priv_utf8_lead3
3-byte sequence lead-byte prefix.
@ k_priv_uc_4byte
Code points >= need 4 UTF-8 bytes.
@ k_priv_uc_3byte
Code points >= need >= 3 UTF-8 bytes.
@ k_priv_tag_name_cap
Lower-cased tag-name buffer size.
@ k_priv_utf8_mask
Low 6 bits per continuation byte.
@ k_priv_utf8_cont
Continuation-byte prefix.
@ k_priv_entity_min
Shortest valid reference ("<").
@ k_priv_base_dec
Decimal numeric-entity base.
@ k_priv_utf8_sh6
Shift for one continuation byte.
@ k_priv_utf8_sh12
Shift for two continuation bytes.
@ k_priv_base_hex
Hexadecimal numeric-entity base.
@ k_priv_utf8_lead2
2-byte sequence lead-byte prefix.
@ k_priv_uc_2byte
Code points >= need >= 2 UTF-8 bytes.
@ k_priv_uc_max
Highest valid Unicode code point.
static bool internal_bytes_equal(const uint8_t *buf, const char *lit, size_t len)
Test whether buf equals the first len bytes of lit.
bool priv_reflow_tok_starts_with(const uint8_t *buf, size_t i, size_t len, const char *lit)
Test whether buf at i begins with the literal lit.
bool priv_reflow_tok_decode_entity(const char *src, size_t avail, uint32_t *out_cp, size_t *out_used)
Decode one XML entity reference beginning at &.
bool priv_reflow_tok_stash_run(reflow_t *engine, const uint8_t *buf, size_t start, size_t end, uint32_t *out_off, uint32_t *out_len)
Entity-decode and whitespace-collapse a text run into the pool.
static bool internal_stash_one(reflow_t *engine, const uint8_t *buf, size_t i, size_t end, bool *last_ws, size_t *consumed)
Stash one source position of a text run (entity or literal byte).
size_t priv_reflow_tok_utf8_encode(uint32_t cp, uint8_t *dst)
UTF-8 encode a code point into dst (up to 4 bytes).
size_t priv_reflow_tok_find_lit(const uint8_t *buf, size_t i, size_t len, const char *lit)
Return the index of the first occurrence of lit (its start), or len.
bool priv_reflow_tok_emit(reflow_t *engine, reflow_token_kind_t kind, reflow_html_tag_t tag, uint8_t style, uint32_t off, uint32_t len)
Append one token to the engine's token pool.
bool priv_reflow_tok_is_block(reflow_html_tag_t tag)
Test whether a tag is a block-flow container in the v1 subset.
static bool internal_feed_utf8(reflow_t *engine, uint32_t cp, bool *last_ws)
Feed a code point's UTF-8 bytes through the whitespace-collapse.
reflow_html_tag_t priv_reflow_tok_classify(const char *name, size_t len)
Map a (possibly namespace-prefixed) tag name to its enum.
size_t priv_reflow_tok_skip_past(const uint8_t *buf, size_t i, size_t len, const char *lit)
Return the index just past the first occurrence of lit.
uint8_t priv_reflow_tok_style_for(reflow_html_tag_t tag)
Return the style bit an inline tag contributes to its subtree.
bool priv_reflow_tok_is_xml_whitespace(char c)
True for the ASCII characters XHTML treats as whitespace.
bool priv_reflow_tok_feed(reflow_t *engine, char ch, bool *last_ws)
Write one byte through the whitespace-collapse state machine.
static bool internal_decode_numeric(const char *src, size_t avail, uint32_t *out_cp, size_t *out_used)
Decode a &#dec; / &#xhex; numeric character reference.
static size_t internal_local_lower(const char *name, size_t len, char *dst)
Lower-case ASCII and copy the local part of a tag name.
reflow_html_tag_t
Recognised HTML element kinds.
@ k_reflow_tag_i
Italic (inline).
@ k_reflow_tag_th
Table header cell.
@ k_reflow_tag_td
Table cell.
@ k_reflow_tag_blockquote
Blockquote indent.
@ k_reflow_tag_p
Paragraph block.
@ k_reflow_tag_h5
Heading level 5.
@ k_reflow_tag_h1
Heading level 1.
@ k_reflow_tag_strong
Bold emphasis (inline).
@ k_reflow_tag_hr
Horizontal rule (void).
@ k_reflow_tag_link
<link> (void; stylesheet ref).
@ k_reflow_tag_h4
Heading level 4.
@ k_reflow_tag_tr
Table row.
@ k_reflow_tag_ul
Unordered list block.
@ k_reflow_tag_li
List item.
@ k_reflow_tag_table
Table (grid layout).
@ k_reflow_tag_em
Italic emphasis (inline).
@ k_reflow_tag_b
Bold (inline).
@ k_reflow_tag_h2
Heading level 2.
@ k_reflow_tag_h6
Heading level 6.
@ k_reflow_tag_br
Line break (void).
@ k_reflow_tag_img
Image (placeholder rect).
@ k_reflow_tag_h3
Heading level 3.
@ k_reflow_tag_ol
Ordered list block.
@ k_reflow_tag_a
Anchor (renders underlined).
@ k_reflow_tag_unknown
Anything not in this enum.
@ k_reflow_color_inherit
No per-run CSS colour.
@ k_reflow_max_tokens
Max parsed token count.
@ k_reflow_text_pool_bytes
Bytes of text pool.
reflow_token_kind_t
Parsed token classification.
@ k_reflow_style_italic
Italic face.
@ k_reflow_style_bold
Bold face.
@ k_reflow_style_underline
Underlined run.
Reflow / pagination engine state.
uint8_t text_pool[k_reflow_text_pool_bytes]
Text bytes.
uint32_t text_pool_used
Bytes consumed in text_pool.
uint32_t token_count
Tokens used.
reflow_token_t tokens[k_reflow_max_tokens]
Parsed tokens.
One parsed token in the engine's token stream.
uint16_t css_font_px
Block-start CSS font px (0 = none -> UA default).
uint32_t text_off
Byte offset into the text pool.
uint32_t text_len
Byte length within the text pool.
uint32_t color
0xRRGGBB CSS colour, or k_reflow_color_inherit.
uint8_t kind
reflow_token_kind_t.
uint8_t tag
reflow_html_tag_t (0 if N/A).
uint8_t style
Font-style bitmask.
uint16_t reserved16
Padding to 4-byte stride.
uint8_t reserved
Block align / <a> link id.