ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_css_rules.c
Go to the documentation of this file.
1
22
23#include <stddef.h>
24#include <string.h>
25
26#include "ra8_attributes.h"
27#include "reflow.h" /* reflow_html_tag_t */
28#include "reflow_css.h"
29#include "reflow_css_internal.h"
30#include "reflow_tokenize_internal.h" /* priv_reflow_tok_classify */
31
32/* ===========================================================================
33 * Internal constants (no magic numbers)
34 * ===========================================================================
35 */
36
41typedef enum : uint8_t {
44
45/* ===========================================================================
46 * Selector parsing
47 * ===========================================================================
48 */
49
73static bool internal_is_name_char(char c)
74{
75 return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) ||
76 (c == '-') || (c == '_');
77}
78
107static bool internal_intern_name(ra8_css_sheet_t* sheet, const char* s, size_t len, uint16_t* off)
108{
109 /* mcdc-deactivated: every caller guards the length before interning -- (nlen == 0U) reject at the selector site, (n > 0U) at the @font-face/font-family sites, and internal_extract_url rejects an empty url() -- so (len == 0U) is unreachable; only (len > k_ra8_css_name_max) varies. */
110 if ((len == 0U) || (len > (size_t)k_ra8_css_name_max)) {
111 return false;
112 }
113 if (((size_t)sheet->names_used + len) > (size_t)k_ra8_css_name_pool) {
114 return false;
115 }
116 *off = sheet->names_used;
117 (void)memcpy(&sheet->names[sheet->names_used], s, len);
118 sheet->names_used = (uint16_t)(sheet->names_used + len);
119 return true;
120}
121
151static bool internal_parse_sel_type(const char* s, size_t len, size_t* i, ra8_css_rule_t* rule)
152{
153 if (s[*i] == '*') {
154 ++(*i); /* universal: no type constraint */
155 return true;
156 }
157 size_t start = *i;
158 /* Bounded: i advances over name chars, capped by len. */
159 while ((*i < len) && internal_is_name_char(s[*i])) {
160 ++(*i);
161 }
162 const reflow_html_tag_t tag = priv_reflow_tok_classify(&s[start], *i - start);
163 if (tag == k_reflow_tag_unknown) {
164 return false; /* unrecognised tag -> drop the rule */
165 }
166 rule->sel_tag = (uint8_t)tag;
167 return true;
168}
169
202 const char* s,
203 size_t len,
204 size_t* i,
205 ra8_css_rule_t* rule)
206{
207 const char kind = s[*i];
208 ++(*i);
209 const size_t start = *i;
210 while ((*i < len) && internal_is_name_char(s[*i])) {
211 ++(*i);
212 }
213 const size_t nlen = *i - start;
214 uint16_t off = 0U;
215 if ((nlen == 0U) || !internal_intern_name(sheet, &s[start], nlen, &off)) {
216 return false;
217 }
218 if (kind == '.') {
219 if (rule->class_len != 0U) {
220 return false; /* a second class -> unsupported in v1 */
221 }
222 rule->class_off = off;
223 rule->class_len = (uint16_t)nlen;
224 return true;
225 }
226 if (rule->id_len != 0U) {
227 return false; /* a second id */
228 }
229 rule->id_off = off;
230 rule->id_len = (uint16_t)nlen;
231 return true;
232}
233
262static bool
263internal_parse_selector(ra8_css_sheet_t* sheet, const char* s, size_t len, ra8_css_rule_t* rule)
264{
265 if (len == 0U) {
266 return false;
267 }
268 size_t i = 0U;
269 bool any = false;
270 if (internal_is_name_char(s[0]) || (s[0] == '*')) {
271 if (!internal_parse_sel_type(s, len, &i, rule)) {
272 return false;
273 }
274 any = true;
275 }
276 /* Then a run of `.class` / `#id` parts (at most one of each). */
277 while (i < len) {
278 if (((s[i] != '.') && (s[i] != '#')) || !internal_parse_sel_part(sheet, s, len, &i, rule)) {
279 return false; /* combinator / pseudo / dup / bad name -> unsupported */
280 }
281 any = true;
282 }
283 return any;
284}
285
316static int32_t
317internal_split_compounds(const char* s, size_t len, const char** part_p, size_t* part_n)
318{
319 size_t nparts = 0U;
320 size_t i = 0U;
321 /* Bounded: i advances to len; each pass consumes >=1 char after the ws skip. */
322 while (i < len) {
323 /* mcdc-deactivated: internal_split_compounds is only ever called with a whitespace-trimmed selector (priv_reflow_css_trim in internal_parse_selector_list and the block dispatcher), so there is no trailing whitespace for this inner skip to consume up to len; the (i < len) false arm is unreachable on any public path. */
324 while ((i < len) && priv_reflow_css_is_ws(s[i])) {
325 ++i;
326 }
327 if (i >= len) {
328 break;
329 }
330 if (nparts > (size_t)k_ra8_css_max_anc) {
331 return -1; /* more than one subject + k_ra8_css_max_anc ancestor parts */
332 }
333 const size_t start = i;
334 while ((i < len) && !priv_reflow_css_is_ws(s[i])) {
335 ++i;
336 }
337 part_p[nparts] = &s[start];
338 part_n[nparts] = i - start;
339 ++nparts;
340 }
341 return (int32_t)nparts;
342}
343
374 const char* s,
375 size_t len,
376 ra8_css_rule_t* rule)
377{
378 const char* part_p[(size_t)k_ra8_css_max_anc + 1U] = {};
379 size_t part_n[(size_t)k_ra8_css_max_anc + 1U] = {};
380 const int32_t nparts = internal_split_compounds(s, len, part_p, part_n);
381 if (nparts <= 0) {
382 return false;
383 }
384 const size_t np = (size_t)nparts;
385 /* Last part = subject compound. */
386 if (!internal_parse_selector(sheet, part_p[np - 1U], part_n[np - 1U], rule)) {
387 return false;
388 }
389 /* Earlier parts = ancestor constraints (outermost first). */
390 rule->anc_count = (uint8_t)(np - 1U);
391 for (size_t a = 0U; (a + 1U) < np; ++a) {
392 ra8_css_rule_t tmp = {};
393 if (!internal_parse_selector(sheet, part_p[a], part_n[a], &tmp)) {
394 return false;
395 }
396 rule->anc[a].tag = tmp.sel_tag;
397 rule->anc[a].class_off = tmp.class_off;
398 rule->anc[a].class_len = tmp.class_len;
399 rule->anc[a].id_off = tmp.id_off;
400 rule->anc[a].id_len = tmp.id_len;
401 }
402 return true;
403}
404
429static void internal_push_rule(ra8_css_sheet_t* sheet, const ra8_css_rule_t* rule)
430{
431 if (sheet->rule_count >= (uint16_t)k_ra8_css_max_rules) {
432 return;
433 }
434 sheet->rules[sheet->rule_count] = *rule;
435 sheet->rules[sheet->rule_count].order = sheet->next_order;
436 sheet->rule_count = (uint16_t)(sheet->rule_count + 1U);
437 sheet->next_order = (uint16_t)(sheet->next_order + 1U);
438}
439
468 const char* sel,
469 size_t sel_len,
470 ra8_css_style_t decl)
471{
472 size_t i = 0U;
473 while (i < sel_len) {
474 size_t comma = i;
475 while ((comma < sel_len) && (sel[comma] != ',')) {
476 ++comma;
477 }
478 size_t one_len = comma - i;
479 const char* one = priv_reflow_css_trim(&sel[i], &one_len);
480 ra8_css_rule_t rule = {};
481 rule.decl = decl;
482 if (internal_parse_complex_selector(sheet, one, one_len, &rule)) {
483 internal_push_rule(sheet, &rule);
484 }
485 i = comma + 1U;
486 }
487}
488
489/* ===========================================================================
490 * @font-face + font-family parsing + block dispatch
491 * ===========================================================================
492 */
493
496static const char* internal_strip_quotes(const char* s, size_t* len)
497{
498 if ((*len >= 2U) && ((s[0] == '"') || (s[0] == '\'')) && (s[*len - 1U] == s[0])) {
499 *len -= 2U;
500 return &s[1];
501 }
502 return s;
503}
504
533static bool internal_extract_url(const char* val, size_t vlen, const char** url, size_t* ulen)
534{
535 /* Bounded: at most (vlen - 3) windows; i advances by 1 each step. */
536 for (size_t i = 0U; (i + (size_t)k_priv_url_len) <= vlen; ++i) {
537 if (!priv_reflow_css_ci_eq(&val[i], (size_t)k_priv_url_len, "url(")) {
538 continue;
539 }
540 size_t a = i + (size_t)k_priv_url_len;
541 size_t b = a;
542 while ((b < vlen) && (val[b] != ')')) {
543 ++b;
544 }
545 size_t n = b - a;
546 const char* p = priv_reflow_css_trim(&val[a], &n);
547 p = internal_strip_quotes(p, &n);
548 if (n == 0U) {
549 return false;
550 }
551 *url = p;
552 *ulen = n;
553 return true;
554 }
555 return false;
556}
557
581static bool internal_is_bold_kw(const char* val, size_t vlen)
582{
583 return priv_reflow_css_ci_eq(val, vlen, "bold") || priv_reflow_css_ci_eq(val, vlen, "bolder") ||
584 priv_reflow_css_ci_eq(val, vlen, "600") || priv_reflow_css_ci_eq(val, vlen, "700") ||
585 priv_reflow_css_ci_eq(val, vlen, "800") || priv_reflow_css_ci_eq(val, vlen, "900");
586}
587
611static bool internal_is_italic_kw(const char* val, size_t vlen)
612{
613 return priv_reflow_css_ci_eq(val, vlen, "italic") || priv_reflow_css_ci_eq(val, vlen, "oblique");
614}
615
647 const char* prop,
648 size_t plen,
649 const char* val,
650 size_t vlen,
651 ra8_css_fontface_t* face)
652{
653 if (priv_reflow_css_ci_eq(prop, plen, "font-family")) {
654 size_t n = vlen;
655 const char* fam = internal_strip_quotes(val, &n);
656 uint16_t off = 0U;
657 if ((n > 0U) && internal_intern_name(sheet, fam, n, &off)) {
658 face->family_off = off;
659 face->family_len = (uint16_t)n;
660 }
661 } else if (priv_reflow_css_ci_eq(prop, plen, "font-weight")) {
662 face->weight_bold = internal_is_bold_kw(val, vlen) ? 1U : 0U;
663 } else if (priv_reflow_css_ci_eq(prop, plen, "font-style")) {
664 face->style_italic = internal_is_italic_kw(val, vlen) ? 1U : 0U;
665 } else if (priv_reflow_css_ci_eq(prop, plen, "src")) {
666 const char* url = nullptr;
667 size_t ulen = 0U;
668 uint16_t off = 0U;
669 if (internal_extract_url(val, vlen, &url, &ulen) &&
670 internal_intern_name(sheet, url, ulen, &off)) {
671 face->src_off = off;
672 face->src_len = (uint16_t)ulen;
673 }
674 } else {
675 /* Other @font-face descriptors (unicode-range, ...) -> ignored. */
676 }
677}
678
680typedef void (
681 *priv_decl_fn)(void* ctx, const char* prop, size_t plen, const char* val, size_t vlen);
682
708static void internal_for_each_decl(const char* s, size_t len, priv_decl_fn cb, void* ctx)
709{
710 size_t i = 0U;
711 /* Bounded: each pass advances past the next ';' (or to len). */
712 while (i < len) {
713 size_t semi = i;
714 while ((semi < len) && (s[semi] != ';')) {
715 ++semi;
716 }
717 size_t colon = i;
718 while ((colon < semi) && (s[colon] != ':')) {
719 ++colon;
720 }
721 if (colon < semi) {
722 size_t plen = colon - i;
723 const char* prop = priv_reflow_css_trim(&s[i], &plen);
724 size_t vlen = semi - (colon + 1U);
725 const char* val = priv_reflow_css_trim(&s[colon + 1U], &vlen);
726 if ((plen > 0U) && (vlen > 0U)) {
727 cb(ctx, prop, plen, val, vlen);
728 }
729 }
730 i = semi + 1U;
731 }
732}
733
739
765static void internal_face_cb(void* ctx, const char* prop, size_t plen, const char* val, size_t vlen)
766{
768 internal_face_apply(c->sheet, prop, plen, val, vlen, c->face);
769}
770
797static void internal_parse_fontface(ra8_css_sheet_t* sheet, const char* block, size_t len)
798{
799 if (sheet->face_count >= (uint16_t)k_ra8_css_max_faces) {
800 return;
801 }
802 ra8_css_fontface_t face = {};
803 priv_face_ctx_t ctx = {.sheet = sheet, .face = &face};
804 internal_for_each_decl(block, len, internal_face_cb, &ctx);
805 if ((face.family_len != 0U) && (face.src_len != 0U)) {
806 sheet->faces[sheet->face_count] = face;
807 sheet->face_count = (uint16_t)(sheet->face_count + 1U);
808 }
809}
810
816
845static void
846internal_family_cb(void* ctx, const char* prop, size_t plen, const char* val, size_t vlen)
847{
849 if (!priv_reflow_css_ci_eq(prop, plen, "font-family")) {
850 return;
851 }
852 size_t n = vlen;
853 const char* fam = internal_strip_quotes(val, &n);
854 uint16_t off = 0U;
855 if ((n > 0U) && internal_intern_name(c->sheet, fam, n, &off)) {
856 c->decl->set = (uint8_t)(c->decl->set | (uint8_t)k_ra8_css_set_family);
857 c->decl->family_off = off;
858 c->decl->family_len = (uint16_t)n;
859 }
860}
861
888 const char* block,
889 size_t len,
890 ra8_css_style_t* decl)
891{
892 priv_family_ctx_t ctx = {.sheet = sheet, .decl = decl};
894}
895
922 const char* sel,
923 size_t sel_len,
924 const char* block,
925 size_t block_len)
926{
927 if (priv_reflow_css_ci_eq(sel, sel_len, "@font-face")) {
928 internal_parse_fontface(sheet, block, block_len);
929 }
930 /* @media / @import / @page / ... are out of v1 scope -> skipped. */
931}
932
959 const char* sel,
960 size_t sel_len,
961 const char* block,
962 size_t block_len)
963{
964 size_t tlen = sel_len;
965 const char* tsel = priv_reflow_css_trim(sel, &tlen);
966 if ((tlen > 0U) && (tsel[0] == '@')) {
967 internal_parse_at_rule(sheet, tsel, tlen, block, block_len);
968 return;
969 }
970 ra8_css_style_t decl = {};
971 priv_reflow_css_parse_decls(block, block_len, &decl);
972 internal_extract_family(sheet, block, block_len, &decl);
973 internal_parse_selector_list(sheet, sel, sel_len, decl);
974}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
HTML / CSS reflow + paginate engine for the ra8d2 ereader.
void priv_reflow_css_parse_decls(const char *s, size_t len, ra8_css_style_t *out)
Parse a prop:value; ... declaration body (no braces) into out.
Definition reflow_css.c:774
const char * priv_reflow_css_trim(const char *s, size_t *len)
Trim leading + trailing whitespace from s[0..*len); returns start.
Definition reflow_css.c:228
bool priv_reflow_css_ci_eq(const char *s, size_t len, const char *lit)
Case-insensitive compare of span s[0..len) against NUL literal lit.
Definition reflow_css.c:162
bool priv_reflow_css_is_ws(char c)
True for CSS whitespace (space / tab / newline / CR / FF).
Definition reflow_css.c:106
Minimal content-CSS cascade for the reflow ereader engine (#111).
@ k_ra8_css_set_family
font-family was declared.
Definition reflow_css.h:98
@ k_ra8_css_name_pool
Bytes of class / id / font name storage.
Definition reflow_css.h:67
@ k_ra8_css_name_max
Max bytes of one class / id / font name.
Definition reflow_css.h:69
@ k_ra8_css_max_anc
Max descendant-ancestor parts per selector.
Definition reflow_css.h:71
@ k_ra8_css_max_faces
Max @font-face rules kept per sheet.
Definition reflow_css.h:70
@ k_ra8_css_max_rules
Max rules kept across all <style> blocks.
Definition reflow_css.h:66
Cross-TU surface for the split content-CSS cascade (#111).
static bool internal_parse_complex_selector(ra8_css_sheet_t *sheet, const char *s, size_t len, ra8_css_rule_t *rule)
Parse a (possibly descendant) selector string into rule.
static bool internal_parse_selector(ra8_css_sheet_t *sheet, const char *s, size_t len, ra8_css_rule_t *rule)
Parse ONE trimmed compound selector into rule; false if unsupported.
void priv_reflow_css_parse_one_block(ra8_css_sheet_t *sheet, const char *sel, size_t sel_len, const char *block, size_t block_len)
Dispatch one selector|@rule { block }: at-rule vs.
static void internal_parse_at_rule(ra8_css_sheet_t *sheet, const char *sel, size_t sel_len, const char *block, size_t block_len)
Route an at-rule: parse @font-face; skip every other @-rule.
static void internal_for_each_decl(const char *s, size_t len, priv_decl_fn cb, void *ctx)
Iterate prop:value; pairs in a declaration block (no braces).
static bool internal_parse_sel_part(ra8_css_sheet_t *sheet, const char *s, size_t len, size_t *i, ra8_css_rule_t *rule)
Parse one .class / #id part at s[*i] into rule; advance i.
priv_css_face_t
@font-face parsing constants.
@ k_priv_url_len
Length of the url( token prefix.
static bool internal_intern_name(ra8_css_sheet_t *sheet, const char *s, size_t len, uint16_t *off)
Intern a class/id name into the sheet pool; false if it does not fit.
static bool internal_is_italic_kw(const char *val, size_t vlen)
True for a font-style keyword that selects the italic face.
static void internal_extract_family(ra8_css_sheet_t *sheet, const char *block, size_t len, ra8_css_style_t *decl)
Scan a rule's declaration block for font-family, interning it.
static const char * internal_strip_quotes(const char *s, size_t *len)
Strip one layer of matching '/" quotes from span s[0..*len).
static bool internal_is_bold_kw(const char *val, size_t vlen)
True for a font-weight keyword that selects the bold face.
static bool internal_is_name_char(char c)
True if c can appear inside a bare type / class / id name.
static int32_t internal_split_compounds(const char *s, size_t len, const char **part_p, size_t *part_n)
Split s into whitespace-separated compound spans.
void(* priv_decl_fn)(void *ctx, const char *prop, size_t plen, const char *val, size_t vlen)
Callback invoked by internal_for_each_decl on each prop:value pair.
static void internal_push_rule(ra8_css_sheet_t *sheet, const ra8_css_rule_t *rule)
Append one fully-built rule to the sheet; drop silently if full.
static bool internal_parse_sel_type(const char *s, size_t len, size_t *i, ra8_css_rule_t *rule)
Parse the optional leading type / * of a compound selector.
static void internal_face_apply(ra8_css_sheet_t *sheet, const char *prop, size_t plen, const char *val, size_t vlen, ra8_css_fontface_t *face)
Apply one @font-face declaration to the face being built.
static bool internal_extract_url(const char *val, size_t vlen, const char **url, size_t *ulen)
Extract the path inside the first url(...) of a src value.
static void internal_parse_selector_list(ra8_css_sheet_t *sheet, const char *sel, size_t sel_len, ra8_css_style_t decl)
Parse a comma-grouped selector list sharing declaration decl.
static void internal_parse_fontface(ra8_css_sheet_t *sheet, const char *block, size_t len)
Parse one @font-face { ... } block; append it if family + src set.
static void internal_family_cb(void *ctx, const char *prop, size_t plen, const char *val, size_t vlen)
priv_decl_fn adapter that interns a rule's font-family value.
static void internal_face_cb(void *ctx, const char *prop, size_t plen, const char *val, size_t vlen)
priv_decl_fn adapter that routes one descriptor to internal_face_apply().
Test-access surface for reflow_tokenize.c internal helpers.
reflow_html_tag_t priv_reflow_tok_classify(const char *name, size_t len)
Map a (possibly namespace-prefixed) tag name to its enum.
reflow_html_tag_t
Recognised HTML element kinds.
@ k_reflow_tag_unknown
Anything not in this enum.
Context threading a sheet + the face being built through the loop.
ra8_css_sheet_t * sheet
Sheet receiving interned name bytes.
ra8_css_fontface_t * face
Face accumulating descriptors.
Context threading a sheet + the rule declaration through the loop.
ra8_css_style_t * decl
Declaration receiving the family slice.
ra8_css_sheet_t * sheet
Sheet receiving the interned family name.
uint16_t id_off
Id name slice offset (id_len 0 = none).
Definition reflow_css.h:150
uint16_t class_len
Class name slice length, bytes.
Definition reflow_css.h:149
uint16_t id_len
Id name slice length, bytes.
Definition reflow_css.h:151
uint16_t class_off
Class name slice offset (class_len 0 = none).
Definition reflow_css.h:148
uint8_t tag
Type tag, or k_reflow_tag_unknown = no type.
Definition reflow_css.h:146
One parsed @font-face rule: family + weight/style + src href.
Definition reflow_css.h:195
uint16_t src_len
src href slice length.
Definition reflow_css.h:199
uint8_t weight_bold
1 = this face is the bold weight.
Definition reflow_css.h:200
uint16_t family_len
font-family name slice length.
Definition reflow_css.h:197
uint8_t style_italic
1 = this face is italic / oblique.
Definition reflow_css.h:201
uint16_t src_off
src href slice offset.
Definition reflow_css.h:198
uint16_t family_off
font-family name slice offset.
Definition reflow_css.h:196
One selector { ... } rule: a (possibly descendant) selector plus its declarations.
Definition reflow_css.h:173
uint16_t class_off
Class name slice offset (class_len 0 = none).
Definition reflow_css.h:176
uint16_t id_off
Id name slice offset (id_len 0 = none).
Definition reflow_css.h:178
ra8_css_style_t decl
Declared properties.
Definition reflow_css.h:182
ra8_css_anc_t anc[k_ra8_css_max_anc]
Descendant ancestor parts.
Definition reflow_css.h:181
uint16_t class_len
Class name slice length, bytes.
Definition reflow_css.h:177
uint16_t order
Source order (lower = earlier; ties to later).
Definition reflow_css.h:180
uint8_t anc_count
Ancestor parts in anc (0 = simple).
Definition reflow_css.h:175
uint16_t id_len
Id name slice length, bytes.
Definition reflow_css.h:179
uint8_t sel_tag
Type tag, or k_reflow_tag_unknown = no type.
Definition reflow_css.h:174
A parsed stylesheet: rule + @font-face arrays and a name byte pool.
Definition reflow_css.h:212
uint16_t face_count
@font-face rules in use.
Definition reflow_css.h:216
uint16_t rule_count
Rules in use.
Definition reflow_css.h:215
ra8_css_rule_t rules[k_ra8_css_max_rules]
Parsed rules.
Definition reflow_css.h:213
uint16_t next_order
Running source-order ctr.
Definition reflow_css.h:217
uint16_t names_used
Bytes used in names.
Definition reflow_css.h:218
ra8_css_fontface_t faces[k_ra8_css_max_faces]
Parsed @font-face rules.
Definition reflow_css.h:214
uint8_t names[k_ra8_css_name_pool]
class/id/font name bytes.
Definition reflow_css.h:219
A declared or computed style: a presence mask plus property values.
Definition reflow_css.h:122
uint16_t family_len
font-family name slice length (0 = none).
Definition reflow_css.h:132
uint8_t set
ra8_css_set_t bitmask: which properties are present.
Definition reflow_css.h:123
uint16_t family_off
font-family name slice offset (valid iff set&family).
Definition reflow_css.h:131