ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_rabook_xml_shim.c
Go to the documentation of this file.
1
15#include "ra8_rabook_xml_shim.h"
16
17#include <stddef.h>
18#include <stdint.h>
19#include <string.h>
20
21#include "ra8_attributes.h"
22#ifdef UNIT_TEST
24#endif
25
27typedef struct {
28 uint32_t offset;
29 uint16_t depth;
31
49 const uint8_t* source,
50 size_t source_len,
51 xml_span_t span)
52{
53 if (ctx->failed) {
54 return (uint32_t)k_book_nil;
55 }
56 size_t decoded = 0U;
57 if (xml_decoded_size(source, source_len, span, &decoded) != k_ra8_ok) {
58 ctx->failed = true;
59 return (uint32_t)k_book_nil;
60 }
61 if ((decoded + 1U) > (size_t)(ctx->buf.string_cap - ctx->string_size)) {
62 ctx->failed = true;
63 return (uint32_t)k_book_nil;
64 }
65 char* const scratch = &ctx->buf.string_pool[ctx->string_size];
66 size_t written = 0U;
67 if (xml_decode(source,
68 source_len,
69 span,
70 scratch,
71 (size_t)(ctx->buf.string_cap - ctx->string_size),
72 &written) != k_ra8_ok) {
73 ctx->failed = true;
74 return (uint32_t)k_book_nil;
75 }
76 uint32_t scan = 0U;
77 while (scan < ctx->string_size) {
78 const char* candidate = &ctx->buf.string_pool[scan];
79 const size_t length = strlen(candidate);
80 if (length == written) {
81 size_t equal_bytes = 0U;
82 while ((equal_bytes < written) && (candidate[equal_bytes] == scratch[equal_bytes])) {
83 equal_bytes++;
84 }
85 if (equal_bytes == written) {
86 return scan;
87 }
88 }
89 scan += (uint32_t)length + 1U;
90 }
91 const uint32_t result = ctx->string_size;
92 ctx->string_size += (uint32_t)written + 1U;
93 return result;
94}
95
115RA8_INTERNAL static ra8_err_t internal_attributes(const uint8_t* source,
116 size_t source_len,
117 const xml_event_t* event,
118 ra8_rabook_ctx_t* ctx,
120{
121 if (event->attribute_count > (uint16_t)k_ra8_rabook_xml_max_attributes) {
122 ctx->failed = true;
123 return k_ra8_err_no_mem;
124 }
125 xml_attr_cursor_t cursor = {};
126 xml_attr_begin(event, &cursor);
127 for (uint16_t i = 0U; i < event->attribute_count; ++i) {
128 xml_attribute_t attribute = {};
129 bool has_value = false;
130 const ra8_err_t attr_err =
131 xml_attr_next(source, source_len, event, &cursor, &attribute, &has_value);
132 if (attr_err != k_ra8_ok) {
134 }
135 if (!has_value) {
137 }
138 workspace->attributes[i].name_off =
139 internal_intern_span(ctx, source, source_len, attribute.name);
140 workspace->attributes[i].value_off =
141 internal_intern_span(ctx, source, source_len, attribute.value);
142 if (ctx->failed) {
143 return k_ra8_err_no_mem;
144 }
145 }
146 return k_ra8_ok;
147}
148
168 uint16_t parent_level,
169 uint32_t next_sibling)
170{
171 const uint32_t previous_node = workspace->last_children[parent_level];
172 const ra8_err_t err =
173 (previous_node == (uint32_t)k_book_nil)
174 ? ra8_rabook_link_child(ctx, workspace->element_nodes[parent_level], next_sibling)
175 : ra8_rabook_link_sibling(ctx, previous_node, next_sibling);
176 if (err == k_ra8_ok) {
177 workspace->last_children[parent_level] = next_sibling;
178 }
179 return err;
180}
181
203RA8_INTERNAL static ra8_err_t internal_element(const uint8_t* source,
204 size_t source_len,
205 const xml_event_t* event,
206 uint16_t selected_depth,
207 ra8_rabook_ctx_t* ctx,
209 uint32_t* out_node)
210{
211 const ra8_err_t attr_err = internal_attributes(source, source_len, event, ctx, workspace);
212 if (attr_err != k_ra8_ok) {
213 return attr_err;
214 }
215 const uint32_t name = internal_intern_span(ctx, source, source_len, event->name);
216 const uint32_t node =
217 ra8_rabook_add_element(ctx, name, workspace->attributes, event->attribute_count);
218 if (node == (uint32_t)k_book_nil) {
219 return k_ra8_err_no_mem;
220 }
221 const uint16_t level = (uint16_t)(event->depth - selected_depth);
222 if (level > 0U) {
223 if (internal_link(ctx, workspace, (uint16_t)(level - 1U), node) != k_ra8_ok) {
224 return k_ra8_err_no_mem;
225 }
226 }
227 workspace->element_nodes[level] = node;
228 workspace->last_children[level] = (uint32_t)k_book_nil;
229 *out_node = node;
230 return k_ra8_ok;
231}
232
253RA8_INTERNAL static ra8_err_t internal_text(const uint8_t* source,
254 size_t source_len,
255 const xml_event_t* event,
256 uint16_t selected_depth,
257 ra8_rabook_ctx_t* ctx,
259{
260 size_t decoded = 0U;
261 if (xml_decoded_size(source, source_len, event->markup, &decoded) != k_ra8_ok) {
263 }
264 if (decoded == 0U) {
265 return k_ra8_ok;
266 }
267 const uint32_t text = internal_intern_span(ctx, source, source_len, event->markup);
268 const uint32_t node = ra8_rabook_add_text(ctx, text);
269 if (node == (uint32_t)k_book_nil) {
270 return k_ra8_err_no_mem;
271 }
272 const uint16_t parent_level = (uint16_t)(event->depth - selected_depth - 1U);
273 return (internal_link(ctx, workspace, parent_level, node) == k_ra8_ok) ? k_ra8_ok
275}
276
300RA8_INTERNAL static ra8_err_t internal_select_event(const uint8_t* source,
301 size_t length,
302 const xml_event_t* event,
303 uint16_t* root_depth,
304 bool* saw_root,
305 uint16_t* direct_children,
306 priv_selection_t* out,
307 bool* out_done)
308{
309 if (!*saw_root) {
310 if (event->kind == (uint8_t)k_xml_event_start) {
311 *saw_root = true;
312 *root_depth = event->depth;
313 *out = (priv_selection_t){event->markup.offset, event->depth};
314 }
315 return k_ra8_ok;
316 }
317 if (event->kind != (uint8_t)k_xml_event_start) {
318 return k_ra8_ok;
319 }
320 if (event->depth != (uint16_t)(*root_depth + 1U)) {
321 return k_ra8_ok;
322 }
323 if (*direct_children >= (uint16_t)k_ra8_rabook_xml_body_siblings) {
325 }
326 if (xml_span_equal(source, length, event->name, "body")) {
327 *out = (priv_selection_t){event->markup.offset, event->depth};
328 *out_done = true;
329 return k_ra8_ok;
330 }
331 ++*direct_children;
332 return k_ra8_ok;
333}
334
355internal_select(const uint8_t* source, size_t length, xml_workspace_t* xml, priv_selection_t* out)
356{
357 xml_reader_t reader = {};
358 ra8_err_t err = xml_reader_init(&reader, source, length, xml);
359 uint16_t root_depth = 0U;
360 uint16_t direct_children = 0U;
361 bool saw_root = false;
362 bool done = false;
363 while ((err == k_ra8_ok) && !done) {
364 xml_event_t event = {};
365 err = xml_reader_next(&reader, &event);
366 if (err == k_ra8_ok) {
367 if (event.kind == (uint8_t)k_xml_event_none) {
368 break;
369 }
370 err = internal_select_event(source,
371 length,
372 &event,
373 &root_depth,
374 &saw_root,
375 &direct_children,
376 out,
377 &done);
378 }
379 }
380 return err;
381}
382
406RA8_INTERNAL static ra8_err_t internal_emit_start(const uint8_t* source,
407 size_t length,
408 const priv_selection_t* selection,
409 const xml_event_t* event,
410 ra8_rabook_ctx_t* ctx,
412 bool* active,
413 uint32_t* out_root)
414{
415 uint32_t node = (uint32_t)k_book_nil;
416 const ra8_err_t err =
417 internal_element(source, length, event, selection->depth, ctx, workspace, &node);
418 if (event->markup.offset == selection->offset) {
419 *out_root = node;
420 }
421 if (err != k_ra8_ok) {
422 return err;
423 }
424 if (event->self_closing == 0U) {
425 return err;
426 }
427 if (event->markup.offset != selection->offset) {
428 return err;
429 }
430 *active = false;
431 return err;
432}
433
459RA8_INTERNAL static ra8_err_t internal_emit_event(const uint8_t* source,
460 size_t length,
461 const priv_selection_t* selection,
462 const xml_event_t* event,
463 ra8_rabook_ctx_t* ctx,
465 bool* active,
466 uint32_t* out_root)
467{
468 if (!*active) {
469 if (event->kind != (uint8_t)k_xml_event_start) {
470 return k_ra8_ok;
471 }
472 if (event->markup.offset != selection->offset) {
473 return k_ra8_ok;
474 }
475 *active = true;
476 }
477 if (event->kind == (uint8_t)k_xml_event_start) {
478 return internal_emit_start(source, length, selection, event, ctx, workspace, active, out_root);
479 }
480 if (event->kind == (uint8_t)k_xml_event_text) {
481 return internal_text(source, length, event, selection->depth, ctx, workspace);
482 }
483 if (event->kind == (uint8_t)k_xml_event_end) {
484 if (event->depth == selection->depth) {
485 *active = false;
486 }
487 }
488 return k_ra8_ok;
489}
490
512RA8_INTERNAL static ra8_err_t internal_emit(const uint8_t* source,
513 size_t length,
514 const priv_selection_t* selection,
515 ra8_rabook_ctx_t* ctx,
517 uint32_t* out_root)
518{
519 xml_reader_t reader = {};
520 ra8_err_t err = xml_reader_init(&reader, source, length, &workspace->xml);
521 bool active = false;
522 while (err == k_ra8_ok) {
523 xml_event_t event = {};
524 err = xml_reader_next(&reader, &event);
525 if (err == k_ra8_ok) {
526 if (event.kind == (uint8_t)k_xml_event_none) {
527 break;
528 }
529 err =
530 internal_emit_event(source, length, selection, &event, ctx, workspace, &active, out_root);
531 }
532 }
533 return err;
534}
535
536#ifdef UNIT_TEST
539 size_t source_len,
541{
542 priv_selection_t selection = {};
543 return internal_select(source, source_len, &workspace->xml, &selection);
544}
545
548 size_t source_len,
549 ra8_rabook_ctx_t* ctx,
551{
552 const priv_selection_t selection = {.offset = 0U, .depth = 0U};
553 uint32_t root = (uint32_t)k_book_nil;
554 return internal_emit(source, source_len, &selection, ctx, workspace, &root);
555}
556#endif
557
580internal_parse_chapter_validate_args(const uint8_t* xhtml_bytes,
581 size_t xhtml_len,
582 const ra8_rabook_ctx_t* ctx,
583 const char* chapter_href,
584 const char* chapter_title,
585 const ra8_rabook_xml_workspace_t* workspace)
586{
587 if (xhtml_bytes == nullptr) {
588 return k_ra8_err_null_ptr;
589 }
590 if (ctx == nullptr) {
591 return k_ra8_err_null_ptr;
592 }
593 if (chapter_href == nullptr) {
594 return k_ra8_err_null_ptr;
595 }
596 if (chapter_title == nullptr) {
597 return k_ra8_err_null_ptr;
598 }
599 if (workspace == nullptr) {
600 return k_ra8_err_null_ptr;
601 }
602 if (xhtml_len == 0U) {
604 }
605 return k_ra8_ok;
606}
607
608ra8_err_t ra8_rabook_xml_parse_chapter(const uint8_t* xhtml_bytes,
609 size_t xhtml_len,
610 ra8_rabook_ctx_t* ctx,
611 const char* chapter_href,
612 const char* chapter_title,
614{
615 const ra8_err_t arg_err = internal_parse_chapter_validate_args(xhtml_bytes,
616 xhtml_len,
617 ctx,
618 chapter_href,
619 chapter_title,
620 workspace);
621 if (arg_err != k_ra8_ok) {
622 return arg_err;
623 }
624 const ra8_rabook_ctx_t checkpoint = *ctx;
625 ra8_err_t err = xml_validate(xhtml_bytes, xhtml_len, &workspace->xml);
626 priv_selection_t selection = {};
627 if (err == k_ra8_ok) {
628 err = internal_select(xhtml_bytes, xhtml_len, &workspace->xml, &selection);
629 }
630 uint32_t root = (uint32_t)k_book_nil;
631 if (err == k_ra8_ok) {
632 err = internal_emit(xhtml_bytes, xhtml_len, &selection, ctx, workspace, &root);
633 }
634 if (err != k_ra8_ok) {
635 *ctx = checkpoint;
636 return (err == k_ra8_err_validation_failed) ? err : k_ra8_err_no_mem;
637 }
638 if (ctx->failed) {
639 *ctx = checkpoint;
640 return k_ra8_err_no_mem;
641 }
642 if (root == (uint32_t)k_book_nil) {
643 *ctx = checkpoint;
644 return k_ra8_err_no_mem;
645 }
646 const uint32_t title = ra8_rabook_intern(ctx, chapter_title);
647 const uint32_t href = ra8_rabook_intern(ctx, chapter_href);
648 const uint32_t chapter = ra8_rabook_add_chapter(ctx, title, href, root);
649 if (chapter == (uint32_t)k_book_nil) {
650 *ctx = checkpoint;
651 return k_ra8_err_no_mem;
652 }
653 return k_ra8_ok;
654}
@ k_book_nil
Absent index / "applies to all chapters".
Definition book.h:129
Annotation-attribute framework macros for ra8-firmware.
#define RA8_TEST_HELPER
Mark a symbol as externally-linked but only callable from tests.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ 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_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
size_t strlen(const char *s)
Calculate string length.
static uint32_t internal_intern_span(ra8_rabook_ctx_t *ctx, const uint8_t *source, size_t source_len, xml_span_t span)
Intern one decoded immutable span through the existing string pool.
static ra8_err_t internal_select_event(const uint8_t *source, size_t length, const xml_event_t *event, uint16_t *root_depth, bool *saw_root, uint16_t *direct_children, priv_selection_t *out, bool *out_done)
Apply one reader event to the direct-body selection search.
ra8_err_t ra8_rabook_xml_parse_chapter(const uint8_t *xhtml_bytes, size_t xhtml_len, ra8_rabook_ctx_t *ctx, const char *chapter_href, const char *chapter_title, ra8_rabook_xml_workspace_t *workspace)
Parse one XHTML chapter and append it to ctx.
static ra8_err_t internal_emit_event(const uint8_t *source, size_t length, const priv_selection_t *selection, const xml_event_t *event, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace, bool *active, uint32_t *out_root)
Apply one reader event to the in-progress subtree emission.
static ra8_err_t internal_select(const uint8_t *source, size_t length, xml_workspace_t *xml, priv_selection_t *out)
Find a direct body or fall back to the document root.
static ra8_err_t internal_text(const uint8_t *source, size_t source_len, const xml_event_t *event, uint16_t selected_depth, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace)
Emit one decoded text run beneath the current element.
static ra8_err_t internal_parse_chapter_validate_args(const uint8_t *xhtml_bytes, size_t xhtml_len, const ra8_rabook_ctx_t *ctx, const char *chapter_href, const char *chapter_title, const ra8_rabook_xml_workspace_t *workspace)
Validate the public chapter-parse entry point's arguments.
static ra8_err_t internal_link(ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace, uint16_t parent_level, uint32_t next_sibling)
Link a new node beneath its selected-subtree parent.
static ra8_err_t internal_emit_start(const uint8_t *source, size_t length, const priv_selection_t *selection, const xml_event_t *event, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace, bool *active, uint32_t *out_root)
Handle one start event for the in-progress subtree emission.
static ra8_err_t internal_element(const uint8_t *source, size_t source_len, const xml_event_t *event, uint16_t selected_depth, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace, uint32_t *out_node)
Emit one element event and link it into preorder.
static ra8_err_t internal_attributes(const uint8_t *source, size_t source_len, const xml_event_t *event, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace)
Collect and intern one event's attributes.
static ra8_err_t internal_emit(const uint8_t *source, size_t length, const priv_selection_t *selection, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace, uint32_t *out_root)
Stream the selected subtree into the builder.
Bounded XHTML-to-RABOOK streaming builder.
@ k_ra8_rabook_xml_max_attributes
Attributes accepted per element.
@ k_ra8_rabook_xml_body_siblings
Direct children searched for body.
Test-only seams for post-validation XML reader faults.
ra8_err_t priv_ra8_rabook_xml_select_unvalidated_test(const uint8_t *source, size_t source_len, ra8_rabook_xml_workspace_t *workspace)
Run the XML subtree-selection pass without public validation.
ra8_err_t priv_ra8_rabook_xml_emit_unvalidated_test(const uint8_t *source, size_t source_len, ra8_rabook_ctx_t *ctx, ra8_rabook_xml_workspace_t *workspace)
Run XML subtree emission without public validation or rollback.
uint32_t ra8_rabook_add_element(ra8_rabook_ctx_t *ctx, uint32_t name_off, const book_attr_t *attrs, uint16_t attr_count)
Append an element node with its attributes; return its node index.
ra8_err_t ra8_rabook_link_sibling(ra8_rabook_ctx_t *ctx, uint32_t previous_node, uint32_t next_sibling)
Set previous_node's next sibling to next_sibling.
uint32_t ra8_rabook_add_text(ra8_rabook_ctx_t *ctx, uint32_t text_off)
Append a text node carrying text_off; return its node index.
ra8_err_t ra8_rabook_link_child(ra8_rabook_ctx_t *ctx, uint32_t parent, uint32_t child)
Set parent's first child to child.
uint32_t ra8_rabook_intern(ra8_rabook_ctx_t *ctx, const char *str)
Intern a NUL-terminated UTF-8 string into the pool, de-duplicated.
uint32_t ra8_rabook_add_chapter(ra8_rabook_ctx_t *ctx, uint32_t title_off, uint32_t href_off, uint32_t root_node)
Append a spine chapter; return its chapter index.
uint32_t value_off
String-pool offset of the attribute value.
Definition book.h:319
uint32_t name_off
String-pool offset of the attribute name.
Definition book.h:318
Selected subtree root coordinates.
uint16_t depth
Absolute XML depth.
uint32_t offset
Start-markup offset.
char * string_pool
String-pool byte arena.
uint32_t string_cap
String-pool capacity in bytes.
Builder state: the arenas plus running counts and a sticky-fail flag.
bool failed
Sticky: an arena overflowed.
uint32_t string_size
String-pool bytes used.
ra8_rabook_buffers_t buf
Caller-provided arenas.
Caller-owned storage for one parser invocation.
uint32_t element_nodes[k_xml_workspace_frames]
Element node at each selected-subtree depth.
uint32_t last_children[k_xml_workspace_frames]
Last emitted child at each selected-subtree depth.
xml_workspace_t xml
Strict reader nesting stack (4096 bytes).
book_attr_t attributes[k_ra8_rabook_xml_max_attributes]
Source-order attributes for the element currently emitted.
Mutable cursor for one start event's attributes.
Definition xml.h:87
Source-order attribute view.
Definition xml.h:81
xml_span_t value
Quoted value excluding delimiters.
Definition xml.h:83
xml_span_t name
Attribute name.
Definition xml.h:82
One XML pull event.
Definition xml.h:71
uint16_t attribute_count
Source-order attributes on start.
Definition xml.h:75
xml_span_t markup
Complete markup span, or text payload.
Definition xml.h:72
uint8_t self_closing
One for an empty-element start.
Definition xml.h:77
xml_span_t name
Element name for start/end.
Definition xml.h:73
uint16_t depth
Root is depth zero.
Definition xml.h:74
uint8_t kind
xml_event_kind_t.
Definition xml.h:76
Pull-reader state; initialise before each pass.
Definition xml.h:93
Immutable byte span expressed relative to the source.
Definition xml.h:44
uint32_t offset
First byte offset.
Definition xml.h:45
Exactly bounded caller-owned nesting storage.
Definition xml.h:57
ra8_err_t xml_decoded_size(const uint8_t *source, size_t source_len, xml_span_t span, size_t *out_length)
Measure the entity-decoded byte count of a bounded span.
Definition xml_decode.c:544
void xml_attr_begin(const xml_event_t *event, xml_attr_cursor_t *cursor)
Initialise source-order attribute iteration for a start event.
Definition xml.c:70
ra8_err_t xml_attr_next(const uint8_t *source, size_t source_len, const xml_event_t *event, xml_attr_cursor_t *cursor, xml_attribute_t *out_attribute, bool *out_has_value)
Return the next source-order attribute.
Definition xml.c:181
@ k_xml_event_start
Element start.
Definition xml.h:64
@ k_xml_event_none
No event / end of source.
Definition xml.h:63
@ k_xml_event_text
Character-data run.
Definition xml.h:66
@ k_xml_event_end
Element end.
Definition xml.h:65
ra8_err_t xml_validate(const uint8_t *source, size_t source_len, xml_workspace_t *workspace)
Validate a complete document before any consumer mutation.
Definition xml.c:631
bool xml_span_equal(const uint8_t *source, size_t source_len, xml_span_t span, const char *literal)
Compare a bounded span with an exact ASCII literal.
Definition xml_decode.c:555
ra8_err_t xml_decode(const uint8_t *source, size_t source_len, xml_span_t span, char *destination, size_t capacity, size_t *out_length)
Entity-decode a source span into a bounded NUL-terminated buffer.
Definition xml_decode.c:511
ra8_err_t xml_reader_init(xml_reader_t *reader, const uint8_t *source, size_t source_len, xml_workspace_t *workspace)
Initialise a pull pass over immutable bytes.
Definition xml.c:571
ra8_err_t xml_reader_next(xml_reader_t *reader, xml_event_t *out_event)
Return the next semantic event and validate syntax incrementally.
Definition xml.c:590