ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
xml_decode.c
Go to the documentation of this file.
1
19#include <stddef.h>
20#include <stdint.h>
21#include <string.h>
22
23#include "ra8_attributes.h"
24#include "xml.h"
25#include "xml_internal.h"
26
28typedef struct {
29 size_t position;
30 size_t end;
31 uint8_t pending[4];
32 uint8_t pending_at;
33 uint8_t pending_len;
35
50RA8_INTERNAL static bool internal_xml_char(uint32_t cp)
51{
52 return (cp == k_priv_xml_tab) || (cp == k_priv_xml_line_feed) ||
57}
58
81internal_utf8_lead(uint8_t lead, uint32_t* out_cp, size_t* out_used, uint32_t* out_minimum)
82{
83 uint32_t cp = lead;
84 size_t used = 1U;
85 uint32_t minimum = 0U;
86 if ((lead >= k_priv_utf8_two_lead_min) && (lead <= k_priv_utf8_two_lead_max)) {
87 cp = (uint32_t)(lead & k_priv_utf8_two_payload_mask);
88 used = 2U;
90 } else if ((lead >= k_priv_utf8_three_lead_min) && (lead <= k_priv_utf8_three_lead_max)) {
91 cp = (uint32_t)(lead & k_priv_utf8_three_payload_mask);
92 used = 3U;
94 } else if ((lead >= k_priv_utf8_four_lead_min) && (lead <= k_priv_utf8_four_lead_max)) {
95 cp = (uint32_t)(lead & k_priv_utf8_four_payload_mask);
96 used = 4U;
98 } else if (lead >= k_priv_utf8_continuation_tag) {
100 } else {
101 /* ASCII lead: the seeded cp/used/minimum above already describe it. */
102 }
103 *out_cp = cp;
104 *out_used = used;
105 *out_minimum = minimum;
106 return k_ra8_ok;
107}
108
127RA8_INTERNAL static ra8_err_t internal_utf8_next(const uint8_t* source,
128 size_t end,
129 size_t position,
130 uint32_t* out_cp,
131 size_t* out_used)
132{
133 if (position >= end) {
135 }
136 uint32_t cp = 0U;
137 size_t used = 0U;
138 uint32_t minimum = 0U;
139 const ra8_err_t lead_err = internal_utf8_lead(source[position], &cp, &used, &minimum);
140 if (lead_err != k_ra8_ok) {
141 return lead_err;
142 }
143 if ((position + used) > end) {
145 }
146 for (size_t i = 1U; i < used; ++i) {
147 const uint8_t byte = source[position + i];
150 }
151 cp = (cp << 6U) | (uint32_t)(byte & k_priv_utf8_scalar_mask);
152 }
153 if ((cp < minimum) || !internal_xml_char(cp)) {
155 }
156 *out_cp = cp;
157 *out_used = used;
158 return k_ra8_ok;
159}
160
161bool priv_xml_span_valid(size_t source_len, xml_span_t span)
162{
163 return ((size_t)span.offset <= source_len) &&
164 ((size_t)span.length <= (source_len - (size_t)span.offset));
165}
166
184RA8_INTERNAL static size_t internal_utf8(uint32_t cp, uint8_t out[4])
185{
187 out[0] = (uint8_t)cp;
188 return 1U;
189 }
191 out[0] = (uint8_t)(k_priv_utf8_two_lead_tag | (cp >> 6U));
192 out[1] = (uint8_t)(k_priv_utf8_continuation_tag | (cp & k_priv_utf8_scalar_mask));
193 return 2U;
194 }
196 out[0] = (uint8_t)(k_priv_utf8_three_lead_tag | (cp >> k_priv_utf8_shift_second));
197 out[1] = (uint8_t)(k_priv_utf8_continuation_tag | ((cp >> 6U) & k_priv_utf8_scalar_mask));
198 out[2] = (uint8_t)(k_priv_utf8_continuation_tag | (cp & k_priv_utf8_scalar_mask));
199 return 3U;
200 }
201 out[0] = (uint8_t)(k_priv_utf8_four_lead_tag | (cp >> k_priv_utf8_shift_third));
202 out[1] = (uint8_t)(k_priv_utf8_continuation_tag |
204 out[2] = (uint8_t)(k_priv_utf8_continuation_tag | ((cp >> 6U) & k_priv_utf8_scalar_mask));
205 out[3] = (uint8_t)(k_priv_utf8_continuation_tag | (cp & k_priv_utf8_scalar_mask));
206 return 4U;
207}
208
223RA8_INTERNAL static uint32_t internal_digit(uint8_t c, uint32_t base)
224{
225 const uint32_t value = (uint32_t)c;
226 if ((c >= (uint8_t)'0') && (c <= (uint8_t)'9')) {
227 return value - (uint32_t)(uint8_t)'0';
228 }
229 if ((base == 16U) && (c >= (uint8_t)'a') && (c <= (uint8_t)'f')) {
230 return (value - (uint32_t)(uint8_t)'a') + k_priv_xml_decimal_base;
231 }
232 if ((base == 16U) && (c >= (uint8_t)'A') && (c <= (uint8_t)'F')) {
233 return (value - (uint32_t)(uint8_t)'A') + k_priv_xml_decimal_base;
234 }
235 return UINT32_MAX;
236}
237
259RA8_PRIV bool
260priv_xml_bytes_equal(const uint8_t* source, size_t offset, const char* literal, size_t length)
261{
262 for (size_t i = 0U; i < length; ++i) {
263 if (source[offset + i] != (uint8_t)literal[i]) {
264 return false;
265 }
266 }
267 return true;
268}
269
288RA8_INTERNAL static ra8_err_t internal_entity(const uint8_t* source,
289 size_t end,
290 size_t position,
291 uint32_t* out_cp,
292 size_t* out_used)
293{
294 static const char* const names[] = {"amp;", "lt;", "gt;", "quot;", "apos;"};
295 static const uint32_t cps[] = {'&', '<', '>', '"', '\''};
296 if ((position + 2U) >= end) {
298 }
299 for (size_t i = 0U; i < (sizeof(names) / sizeof(names[0])); ++i) {
300 const size_t length = strlen(names[i]);
301 if (((position + 1U + length) <= end) &&
302 priv_xml_bytes_equal(source, position + 1U, names[i], length)) {
303 *out_cp = cps[i];
304 *out_used = length + 1U;
305 return k_ra8_ok;
306 }
307 }
308 if (source[position + 1U] != (uint8_t)'#') {
310 }
311 size_t cursor = position + 2U;
312 uint32_t base = k_priv_xml_decimal_base;
313 // mcdc-deactivated: internal_entity radix probe; the `(position + 2U) >= end` precheck at the top of this function has already returned for every shorter span, so `cursor == position + 2U` is strictly less than `end` on every reachable path and the bound condition is constant-true.
314 if ((cursor < end) && ((source[cursor] == (uint8_t)'x') || (source[cursor] == (uint8_t)'X'))) {
315 base = 16U;
316 ++cursor;
317 }
318 const size_t first_digit = cursor;
319 uint32_t cp = 0U;
320 while ((cursor < end) && (source[cursor] != (uint8_t)';')) {
321 const uint32_t digit = internal_digit(source[cursor], base);
322 if ((digit >= base) || (cp > ((k_priv_xml_scalar_max - digit) / base))) {
324 }
325 cp = (cp * base) + digit;
326 ++cursor;
327 }
328 if ((cursor == first_digit) || (cursor >= end) || !internal_xml_char(cp)) {
330 }
331 *out_cp = cp;
332 *out_used = (cursor - position) + 1U;
333 return k_ra8_ok;
334}
335
364RA8_INTERNAL static ra8_err_t internal_decode_one(const uint8_t* source,
365 size_t end,
366 size_t cursor,
367 char* destination,
368 size_t capacity,
369 bool truncate,
370 size_t* output,
371 bool* clipped,
372 size_t* out_used)
373{
374 uint8_t bytes[4] = {};
375 size_t count = 0U;
376 size_t used = 0U;
377 if (source[cursor] == (uint8_t)'&') {
378 uint32_t cp = 0U;
379 ra8_err_t err = internal_entity(source, end, cursor, &cp, &used);
380 if (err != k_ra8_ok) {
381 return err;
382 }
383 count = internal_utf8(cp, bytes);
384 } else {
385 uint32_t cp = 0U;
386 const ra8_err_t err = internal_utf8_next(source, end, cursor, &cp, &used);
387 if (err != k_ra8_ok) {
388 return err;
389 }
390 count = used;
391 (void)memcpy(bytes, &source[cursor], count);
392 }
393 if ((destination != nullptr) && !*clipped && ((*output + count) >= capacity)) {
394 if (!truncate) {
395 return k_ra8_err_no_mem;
396 }
397 *clipped = true;
398 destination[*output] = '\0';
399 }
400 if ((destination != nullptr) && !*clipped) {
401 (void)memcpy(&destination[*output], bytes, count);
402 }
403 if ((destination == nullptr) || !*clipped) {
404 *output += count;
405 }
406 *out_used = used;
407 return k_ra8_ok;
408}
409
430RA8_INTERNAL static ra8_err_t internal_decode(const uint8_t* source,
431 xml_span_t span,
432 char* destination,
433 size_t capacity,
434 bool truncate,
435 size_t* out_length)
436{
437 size_t output = 0U;
438 bool clipped = false;
439 const size_t end = (size_t)span.offset + (size_t)span.length;
440 if ((destination != nullptr) && (capacity == 0U)) {
441 return k_ra8_err_no_mem;
442 }
443 for (size_t cursor = span.offset; cursor < end;) {
444 size_t used = 0U;
445 const ra8_err_t err = internal_decode_one(source,
446 end,
447 cursor,
448 destination,
449 capacity,
450 truncate,
451 &output,
452 &clipped,
453 &used);
454 if (err != k_ra8_ok) {
455 return err;
456 }
457 cursor += used;
458 }
459 if (destination != nullptr) {
460 destination[output] = '\0';
461 }
462 *out_length = output;
463 return k_ra8_ok;
464}
465
483internal_decoded_byte(const uint8_t* source, priv_decode_cursor_t* cursor, uint8_t* out)
484{
485 if (cursor->pending_at < cursor->pending_len) {
486 *out = cursor->pending[cursor->pending_at];
487 cursor->pending_at++;
488 return k_ra8_ok;
489 }
490 if (cursor->position >= cursor->end) {
492 }
493 if (source[cursor->position] != (uint8_t)'&') {
494 *out = source[cursor->position];
495 cursor->position++;
496 return k_ra8_ok;
497 }
498 uint32_t cp = 0U;
499 size_t used = 0U;
500 const ra8_err_t err = internal_entity(source, cursor->end, cursor->position, &cp, &used);
501 if (err != k_ra8_ok) {
502 return err;
503 }
504 cursor->position += used;
505 cursor->pending_len = (uint8_t)internal_utf8(cp, cursor->pending);
506 cursor->pending_at = 1U;
507 *out = cursor->pending[0];
508 return k_ra8_ok;
509}
510
511ra8_err_t xml_decode(const uint8_t* source,
512 size_t source_len,
513 xml_span_t span,
514 char* destination,
515 size_t capacity,
516 size_t* out_length)
517{
518 if ((source == nullptr) || (destination == nullptr) || (out_length == nullptr)) {
519 return k_ra8_err_null_ptr;
520 }
521 if (!priv_xml_span_valid(source_len, span)) {
523 }
524 return internal_decode(source, span, destination, capacity, false, out_length);
525}
526
527ra8_err_t xml_decode_prefix(const uint8_t* source,
528 size_t source_len,
529 xml_span_t span,
530 char* destination,
531 size_t capacity,
532 size_t* out_length)
533{
534 if ((source == nullptr) || (destination == nullptr) || (out_length == nullptr)) {
535 return k_ra8_err_null_ptr;
536 }
537 if (!priv_xml_span_valid(source_len, span)) {
539 }
540 return internal_decode(source, span, destination, capacity, true, out_length);
541}
542
544xml_decoded_size(const uint8_t* source, size_t source_len, xml_span_t span, size_t* out_length)
545{
546 if ((source == nullptr) || (out_length == nullptr)) {
547 return k_ra8_err_null_ptr;
548 }
549 if (!priv_xml_span_valid(source_len, span)) {
551 }
552 return internal_decode(source, span, nullptr, 0U, false, out_length);
553}
554
555bool xml_span_equal(const uint8_t* source, size_t source_len, xml_span_t span, const char* literal)
556{
557 if ((source == nullptr) || (literal == nullptr) || !priv_xml_span_valid(source_len, span)) {
558 return false;
559 }
560 const size_t length = strlen(literal);
561 return ((size_t)span.length == length) &&
562 priv_xml_bytes_equal(source, (size_t)span.offset, literal, length);
563}
564
565bool xml_span_local_equal(const uint8_t* source,
566 size_t source_len,
567 xml_span_t span,
568 const char* literal)
569{
570 if ((source == nullptr) || !priv_xml_span_valid(source_len, span)) {
571 return false;
572 }
573 uint32_t offset = span.offset;
574 uint32_t length = span.length;
575 for (uint32_t i = 0U; i < span.length; ++i) {
576 if (source[span.offset + i] == (uint8_t)':') {
577 offset = span.offset + i + 1U;
578 length = span.length - i - 1U;
579 }
580 }
581 return xml_span_equal(source, source_len, (xml_span_t){offset, length}, literal);
582}
583
584bool xml_decoded_equal(const uint8_t* source, size_t source_len, xml_span_t left, xml_span_t right)
585{
586 if (source == nullptr) {
587 return false;
588 }
589 size_t left_size = 0U;
590 size_t right_size = 0U;
591 if ((xml_decoded_size(source, source_len, left, &left_size) != k_ra8_ok) ||
592 (xml_decoded_size(source, source_len, right, &right_size) != k_ra8_ok) ||
593 (left_size != right_size)) {
594 return false;
595 }
596 priv_decode_cursor_t li = {.position = left.offset, .end = (size_t)left.offset + left.length};
597 priv_decode_cursor_t ri = {.position = right.offset, .end = (size_t)right.offset + right.length};
598 for (size_t i = 0U; i < left_size; ++i) {
599 uint8_t lb = 0U;
600 uint8_t rb = 0U;
601 // mcdc-deactivated: xml_decoded_equal byte-walk status guards; xml_decoded_size already decoded both spans end to end and reported exactly left_size/right_size bytes, so for every i < left_size both internal_decoded_byte() calls are re-walking bytes proven decodable -- only the byte-inequality condition can flip.
602 if ((internal_decoded_byte(source, &li, &lb) != k_ra8_ok) ||
603 (internal_decoded_byte(source, &ri, &rb) != k_ra8_ok) || (lb != rb)) {
604 return false;
605 }
606 }
607 return true;
608}
609
610ra8_err_t priv_xml_raw(const uint8_t* source, size_t start, size_t end)
611{
612 size_t cursor = start;
613 while (cursor < end) {
614 uint32_t cp = 0U;
615 size_t used = 0U;
616 const ra8_err_t err = internal_utf8_next(source, end, cursor, &cp, &used);
617 if (err != k_ra8_ok) {
618 return err;
619 }
620 cursor += used;
621 }
622 return k_ra8_ok;
623}
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_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
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.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Incremental decoded-byte cursor used for allocation-free comparison.
Definition xml_decode.c:28
uint8_t pending_at
Next pending byte.
Definition xml_decode.c:32
uint8_t pending[4]
UTF-8 bytes from the current entity.
Definition xml_decode.c:31
uint8_t pending_len
Pending-byte count.
Definition xml_decode.c:33
size_t end
One-past-last source byte.
Definition xml_decode.c:30
size_t position
Next source byte.
Definition xml_decode.c:29
Immutable byte span expressed relative to the source.
Definition xml.h:44
uint32_t length
Byte count.
Definition xml.h:46
uint32_t offset
First byte offset.
Definition xml.h:45
Bounded, caller-owned, no-heap XML pull reader.
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
bool xml_decoded_equal(const uint8_t *source, size_t source_len, xml_span_t left, xml_span_t right)
Compare two entity-decoded spans from the same immutable source.
Definition xml_decode.c:584
static ra8_err_t internal_decode(const uint8_t *source, xml_span_t span, char *destination, size_t capacity, bool truncate, size_t *out_length)
Decode, prefix-decode, or measure one already-bounded span.
Definition xml_decode.c:430
bool priv_xml_bytes_equal(const uint8_t *source, size_t offset, const char *literal, size_t length)
Compare bounded source bytes against a literal of known length.
Definition xml_decode.c:260
static ra8_err_t internal_utf8_next(const uint8_t *source, size_t end, size_t position, uint32_t *out_cp, size_t *out_used)
Decode one canonical UTF-8 scalar.
Definition xml_decode.c:127
static bool internal_xml_char(uint32_t cp)
Test whether a scalar is an XML 1.0 character.
Definition xml_decode.c:50
bool xml_span_local_equal(const uint8_t *source, size_t source_len, xml_span_t span, const char *literal)
Compare the namespace-local tail of a bounded span with an ASCII literal.
Definition xml_decode.c:565
bool priv_xml_span_valid(size_t source_len, xml_span_t span)
Check that a source-relative span is in range.
Definition xml_decode.c:161
static size_t internal_utf8(uint32_t cp, uint8_t out[4])
Encode one valid Unicode scalar as UTF-8.
Definition xml_decode.c:184
ra8_err_t priv_xml_raw(const uint8_t *source, size_t start, size_t end)
Validate canonical UTF-8 XML 1.0 characters over a byte range.
Definition xml_decode.c:610
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
static ra8_err_t internal_entity(const uint8_t *source, size_t end, size_t position, uint32_t *out_cp, size_t *out_used)
Decode one entity beginning at a bounded position.
Definition xml_decode.c:288
static ra8_err_t internal_utf8_lead(uint8_t lead, uint32_t *out_cp, size_t *out_used, uint32_t *out_minimum)
Classify one UTF-8 lead byte into its scalar width and payload.
Definition xml_decode.c:81
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
static ra8_err_t internal_decode_one(const uint8_t *source, size_t end, size_t cursor, char *destination, size_t capacity, bool truncate, size_t *output, bool *clipped, size_t *out_used)
Decode one character or entity and append it to the destination.
Definition xml_decode.c:364
static uint32_t internal_digit(uint8_t c, uint32_t base)
Convert one numeric-reference digit.
Definition xml_decode.c:223
static ra8_err_t internal_decoded_byte(const uint8_t *source, priv_decode_cursor_t *cursor, uint8_t *out)
Return one entity-decoded byte from a comparison cursor.
Definition xml_decode.c:483
ra8_err_t xml_decode_prefix(const uint8_t *source, size_t source_len, xml_span_t span, char *destination, size_t capacity, size_t *out_length)
Decode the longest complete prefix that fits the destination.
Definition xml_decode.c:527
Private lexical seams shared by the bounded XML reader.
@ k_priv_xml_decimal_base
Numeric-entity decimal radix.
@ k_priv_utf8_shift_second
Shift for the second payload group.
@ k_priv_xml_scalar_max
Last Unicode scalar.
@ k_priv_utf8_continuation_tag
Continuation tag and ASCII ceiling.
@ k_priv_utf8_two_lead_tag
Encoded two-byte lead tag.
@ k_priv_xml_bmp_second_min
First scalar after surrogates.
@ k_priv_utf8_scalar_mask
Scalar bits per continuation byte.
@ k_priv_utf8_three_lead_min
First three-byte lead.
@ k_priv_utf8_shift_third
Shift for the third payload group.
@ k_priv_xml_bmp_second_max
Last permitted BMP scalar.
@ k_priv_xml_line_feed
XML line-feed character.
@ k_priv_utf8_four_lead_min
First four-byte lead.
@ k_priv_xml_printable_min
First ordinary XML character.
@ k_priv_utf8_four_lead_tag
Encoded four-byte lead tag.
@ k_priv_utf8_two_lead_min
First canonical two-byte lead.
@ k_priv_xml_tab
XML tab character.
@ k_priv_utf8_four_lead_max
Last canonical four-byte lead.
@ k_priv_utf8_two_payload_mask
Payload bits in a two-byte lead.
@ k_priv_utf8_four_payload_mask
Payload bits in a four-byte lead.
@ k_priv_utf8_three_lead_tag
Encoded three-byte lead tag.
@ k_priv_utf8_three_payload_mask
Payload bits in a three-byte lead.
@ k_priv_utf8_three_lead_max
Last three-byte lead.
@ k_priv_utf8_three_scalar_min
First scalar needing three bytes.
@ k_priv_xml_bmp_first_max
Last scalar before surrogates.
@ k_priv_xml_carriage_return
XML carriage-return character.
@ k_priv_utf8_continuation_mask
Continuation tag mask.
@ k_priv_utf8_two_lead_max
Last two-byte lead.
@ k_priv_xml_supplementary_min
First supplementary scalar.