ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_utf.c
Go to the documentation of this file.
1
19
20#include <stddef.h>
21#include <stdint.h>
22
23#include "ra8_attributes.h"
24#include "ra8_err.h"
25#include "ra8_fs_fat_internal.h"
26#include "ra8_fs_utf_internal.h"
27
28/* Every UTF-8 name buffer in this module is sized from a UTF-16 unit cap times
29 * the worst case per unit, plus a terminator. Asserting it here rather than
30 * trusting the arithmetic in a comment is what stops the two drifting when one
31 * of the unit caps moves: a name that fits on disk MUST fit in the buffer it is
32 * handed back in, or the conversion reports ::k_ra8_err_no_mem for a name the
33 * volume holds perfectly well. */
34static_assert((uint32_t)k_lfn_utf8_cap ==
35 (((uint32_t)k_utf8_max_per_unit * (uint32_t)k_lfn_write_max) + 1U),
36 "k_lfn_utf8_cap must hold the longest VFAT long name in UTF-8");
37static_assert((uint32_t)k_exfat_name_u8_cap ==
38 (((uint32_t)k_utf8_max_per_unit * (uint32_t)k_exfat_name_cap) + 1U),
39 "k_exfat_name_u8_cap must hold the longest exFAT name in UTF-8");
40
41/* =============================================================================
42 * UTF-8 -> UTF-16
43 * =============================================================================
44 */
45
72static uint8_t internal_utf8_lead(uint8_t b, uint32_t* out_len, uint32_t* out_cp)
73{
74 const uint32_t u = (uint32_t)b;
75 if (u <= (uint32_t)k_utf_ascii_max) {
76 *out_len = (uint32_t)k_utf_len_1;
77 *out_cp = u;
78 return 1U;
79 }
80 if ((u & (uint32_t)k_utf_lead2_mask) == (uint32_t)k_utf_lead2_tag) {
81 *out_len = (uint32_t)k_utf_len_2;
82 *out_cp = u & (uint32_t)k_utf_lead2_payload;
83 return 1U;
84 }
85 if ((u & (uint32_t)k_utf_lead3_mask) == (uint32_t)k_utf_lead3_tag) {
86 *out_len = (uint32_t)k_utf_len_3;
87 *out_cp = u & (uint32_t)k_utf_lead3_payload;
88 return 1U;
89 }
90 if ((u & (uint32_t)k_utf_lead4_mask) == (uint32_t)k_utf_lead4_tag) {
91 *out_len = (uint32_t)k_utf_len_4;
92 *out_cp = u & (uint32_t)k_utf_lead4_payload;
93 return 1U;
94 }
95 return 0U;
96}
97
125static uint8_t internal_utf8_tail(const char* in, uint32_t lead, uint32_t len, uint32_t* io_cp)
126{
127 for (uint32_t k = 1U; k < len; k++) {
128 const uint32_t b = (uint32_t)(unsigned char)in[lead + k];
129 if ((b & (uint32_t)k_utf_cont_mask) != (uint32_t)k_utf_cont_tag) {
130 return 0U;
131 }
132 *io_cp = (*io_cp << (uint32_t)k_utf_cont_shift) | (b & (uint32_t)k_utf_cont_payload);
133 }
134 return 1U;
135}
136
163static uint8_t internal_utf8_wellformed(uint32_t cp, uint32_t len)
164{
165 if (len == (uint32_t)k_utf_len_2) {
166 return (cp >= (uint32_t)k_utf_min_2byte) ? 1U : 0U;
167 }
168 if (len == (uint32_t)k_utf_len_3) {
169 if (cp < (uint32_t)k_utf_min_3byte) {
170 return 0U;
171 }
172 /* CESU-8 / WTF-8 spell a surrogate as three bytes; UTF-8 never does. */
173 if ((cp >= (uint32_t)k_utf_sur_hi_first) && (cp <= (uint32_t)k_utf_sur_last)) {
174 return 0U;
175 }
176 return 1U;
177 }
178 if (len == (uint32_t)k_utf_len_4) {
179 if (cp < (uint32_t)k_utf_min_4byte) {
180 return 0U;
181 }
182 return (cp <= (uint32_t)k_utf_code_max) ? 1U : 0U;
183 }
184 return 1U; /* one-byte forms are ASCII by construction */
185}
186
214static ra8_err_t internal_utf8_next(const char* in, uint32_t* io_pos, uint32_t* out_cp)
215{
216 const uint32_t pos = *io_pos;
217 uint32_t len = 0U;
218 uint32_t cp = 0U;
219 if (internal_utf8_lead((uint8_t)(unsigned char)in[pos], &len, &cp) == 0U) {
221 }
222 if (internal_utf8_tail(in, pos, len, &cp) == 0U) {
224 }
225 if (internal_utf8_wellformed(cp, len) == 0U) {
227 }
228 *io_pos = pos + len;
229 *out_cp = cp;
230 return k_ra8_ok;
231}
232
259static ra8_err_t internal_utf16_put(uint32_t cp, uint16_t* out, uint32_t cap, uint32_t* io_n)
260{
261 const uint32_t n = *io_n;
262 /* One assignment of a single constant per arm, rather than a ternary whose
263 * composite value MISRA 10.6 forbids assigning to the wider uint32_t. */
264 uint32_t need = 1U;
265 if (cp >= (uint32_t)k_utf_min_4byte) {
266 need = 2U;
267 }
268 if ((n + need) > cap) {
269 return k_ra8_err_no_mem;
270 }
271 if (need == 1U) {
272 out[n] = (uint16_t)cp;
273 } else {
274 const uint32_t rest = cp - (uint32_t)k_utf_min_4byte;
275 out[n] = (uint16_t)((uint32_t)k_utf_sur_hi_first + (rest >> (uint32_t)k_utf_sur_shift));
276 out[n + 1U] = (uint16_t)((uint32_t)k_utf_sur_lo_first + (rest & (uint32_t)k_utf_sur_mask));
277 }
278 *io_n = n + need;
279 return k_ra8_ok;
280}
281
282/* `priv_utf8_to_utf16()`: see header for the documented contract. */
283ra8_err_t priv_utf8_to_utf16(const char* in, uint16_t* out, uint32_t cap, uint32_t* out_units)
284{
285 if ((in == nullptr) || (out == nullptr) || (out_units == nullptr)) {
286 return k_ra8_err_null_ptr;
287 }
288 *out_units = 0U;
289 uint32_t pos = 0U;
290 uint32_t n = 0U;
291 /* Bounded (NASA Rule 2): every pass either returns or appends at least one
292 * unit, and ::priv_utf16_put refuses to grow past `cap`. */
293 while (n <= cap) {
294 if (in[pos] == '\0') {
295 *out_units = n;
296 return k_ra8_ok;
297 }
298 uint32_t cp = 0U;
299 ra8_err_t err = internal_utf8_next(in, &pos, &cp);
300 if (err != k_ra8_ok) {
301 return err;
302 }
303 err = internal_utf16_put(cp, out, cap, &n);
304 if (err != k_ra8_ok) {
305 return err;
306 }
307 }
308 /* Unreachable: the loop's only exits are the three returns above. This is the
309 * Rule 2 bound's exit, not a fourth answer. */
310 return k_ra8_err_no_mem; /* GCOVR_EXCL_LINE -- bounded-loop fallback after exhaustive returns */
311}
312
313/* =============================================================================
314 * UTF-16 -> UTF-8
315 * =============================================================================
316 */
317
346static ra8_err_t
347internal_utf16_take(const uint16_t* in, uint32_t units, uint32_t* io_i, uint32_t* out_cp)
348{
349 const uint32_t i = *io_i;
350 const uint32_t hi = (uint32_t)in[i];
351 if ((hi < (uint32_t)k_utf_sur_hi_first) || (hi > (uint32_t)k_utf_sur_last)) {
352 *out_cp = hi;
353 *io_i = i + 1U;
354 return k_ra8_ok;
355 }
356 if (hi >= (uint32_t)k_utf_sur_lo_first) {
357 return k_ra8_err_invalid_arg; /* a low surrogate with no high one before it */
358 }
359 if ((i + 1U) >= units) {
360 return k_ra8_err_invalid_arg; /* a high surrogate at the end of the name */
361 }
362 const uint32_t lo = (uint32_t)in[i + 1U];
363 if ((lo < (uint32_t)k_utf_sur_lo_first) || (lo > (uint32_t)k_utf_sur_last)) {
364 return k_ra8_err_invalid_arg; /* a high surrogate followed by something else */
365 }
366 *out_cp = (uint32_t)k_utf_min_4byte +
367 (((hi - (uint32_t)k_utf_sur_hi_first) << (uint32_t)k_utf_sur_shift) |
368 (lo - (uint32_t)k_utf_sur_lo_first));
369 *io_i = i + 2U;
370 return k_ra8_ok;
371}
372
396static uint32_t internal_utf8_len_of(uint32_t cp)
397{
398 if (cp < (uint32_t)k_utf_min_2byte) {
399 return (uint32_t)k_utf_len_1;
400 }
401 if (cp < (uint32_t)k_utf_min_3byte) {
402 return (uint32_t)k_utf_len_2;
403 }
404 if (cp < (uint32_t)k_utf_min_4byte) {
405 return (uint32_t)k_utf_len_3;
406 }
407 return (uint32_t)k_utf_len_4;
408}
409
435static void internal_utf8_put_tail(char* out, uint32_t from, uint32_t len, uint32_t cp)
436{
437 uint32_t rest = cp;
438 for (uint32_t k = len - 1U; k >= 1U; k--) {
439 const uint32_t b = (uint32_t)k_utf_cont_tag | (rest & (uint32_t)k_utf_cont_payload);
440 out[from + k] = (char)(unsigned char)b;
441 rest >>= (uint32_t)k_utf_cont_shift;
442 }
443}
444
471static ra8_err_t internal_utf8_put(uint32_t cp, char* out, uint32_t cap, uint32_t* io_n)
472{
473 const uint32_t n = *io_n;
474 const uint32_t len = internal_utf8_len_of(cp);
475 if ((n + len + 1U) > cap) {
476 return k_ra8_err_no_mem;
477 }
478 if (len == (uint32_t)k_utf_len_1) {
479 out[n] = (char)(unsigned char)cp;
480 *io_n = n + len;
481 return k_ra8_ok;
482 }
483 uint32_t lead_tag = (uint32_t)k_utf_lead4_tag;
484 if (len == (uint32_t)k_utf_len_2) {
485 lead_tag = (uint32_t)k_utf_lead2_tag;
486 } else if (len == (uint32_t)k_utf_len_3) {
487 lead_tag = (uint32_t)k_utf_lead3_tag;
488 } else {
489 /* four-byte lead: the initialiser above */
490 }
491 const uint32_t lead_bits = cp >> ((len - 1U) * (uint32_t)k_utf_cont_shift);
492 out[n] = (char)(unsigned char)(lead_tag | lead_bits);
493 internal_utf8_put_tail(out, n, len, cp);
494 *io_n = n + len;
495 return k_ra8_ok;
496}
497
498/* `priv_utf16_to_utf8()`: see header for the documented contract. */
499ra8_err_t priv_utf16_to_utf8(const uint16_t* in, uint32_t units, char* out, uint32_t cap)
500{
501 if ((in == nullptr) || (out == nullptr) || (cap == 0U)) {
502 return k_ra8_err_null_ptr;
503 }
504 out[0] = '\0';
505 uint32_t i = 0U;
506 uint32_t n = 0U;
507 while (i < units) {
508 uint32_t cp = 0U;
509 ra8_err_t err = internal_utf16_take(in, units, &i, &cp);
510 if (err != k_ra8_ok) {
511 return err;
512 }
513 err = internal_utf8_put(cp, out, cap, &n);
514 if (err != k_ra8_ok) {
515 out[0] = '\0';
516 return err;
517 }
518 }
519 out[n] = '\0';
520 return k_ra8_ok;
521}
522
523/* =============================================================================
524 * Folding and inspection
525 * =============================================================================
526 */
527
528/* `priv_utf16_ieq()`: see header for the documented contract. */
529uint8_t priv_utf16_ieq(const uint16_t* a, uint32_t an, const uint16_t* b, uint32_t bn)
530{
531 if (an != bn) {
532 return 0U;
533 }
534 for (uint32_t i = 0U; i < an; i++) {
536 return 0U;
537 }
538 }
539 return 1U;
540}
541
542/* `priv_utf16_all_ascii()`: see header for the documented contract. */
543uint8_t priv_utf16_all_ascii(const uint16_t* in, uint32_t units)
544{
545 for (uint32_t i = 0U; i < units; i++) {
546 if ((uint32_t)in[i] > (uint32_t)k_utf_ascii_max) {
547 return 0U;
548 }
549 }
550 return 1U;
551}
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_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ 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
uint16_t priv_exfat_upcase_unit(uint16_t unit)
Fold a UTF-16 code unit through the canonical exFAT up-case table.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
@ k_exfat_name_u8_cap
That name in UTF-8: 3 * 64, plus a NUL.
@ k_exfat_name_cap
Longest name we store, in UTF-16 units.
@ k_lfn_utf8_cap
A 247-unit name in UTF-8: 3 * 247, + NUL.
@ k_lfn_write_max
Longest name we WRITE (k_lfn_max_entries).
uint8_t priv_utf16_ieq(const uint16_t *a, uint32_t an, const uint16_t *b, uint32_t bn)
Compare two UTF-16 names for case-insensitive equality.
Definition ra8_fs_utf.c:529
uint8_t priv_utf16_all_ascii(const uint16_t *in, uint32_t units)
Is every unit of in inside the ASCII range?
Definition ra8_fs_utf.c:543
ra8_err_t priv_utf8_to_utf16(const char *in, uint16_t *out, uint32_t cap, uint32_t *out_units)
Convert a NUL-terminated UTF-8 name into UTF-16LE code units.
Definition ra8_fs_utf.c:283
static ra8_err_t internal_utf8_put(uint32_t cp, char *out, uint32_t cap, uint32_t *io_n)
Append cp to a UTF-8 buffer, reserving room for the terminator.
Definition ra8_fs_utf.c:471
static ra8_err_t internal_utf8_next(const char *in, uint32_t *io_pos, uint32_t *out_cp)
Decode the sequence at *io_pos, advancing the cursor past it.
Definition ra8_fs_utf.c:214
static void internal_utf8_put_tail(char *out, uint32_t from, uint32_t len, uint32_t cp)
Write the continuation bytes of cp after its lead byte.
Definition ra8_fs_utf.c:435
static uint8_t internal_utf8_tail(const char *in, uint32_t lead, uint32_t len, uint32_t *io_cp)
Fold a sequence's continuation bytes into the code point under assembly.
Definition ra8_fs_utf.c:125
static uint8_t internal_utf8_wellformed(uint32_t cp, uint32_t len)
Is cp a code point that a len byte sequence may legally encode?
Definition ra8_fs_utf.c:163
static uint8_t internal_utf8_lead(uint8_t b, uint32_t *out_len, uint32_t *out_cp)
Classify a UTF-8 lead byte into a sequence length and its payload bits.
Definition ra8_fs_utf.c:72
static ra8_err_t internal_utf16_take(const uint16_t *in, uint32_t units, uint32_t *io_i, uint32_t *out_cp)
Take the character at *io_i, consuming a surrogate pair as one.
Definition ra8_fs_utf.c:347
static uint32_t internal_utf8_len_of(uint32_t cp)
How many UTF-8 bytes does cp occupy?
Definition ra8_fs_utf.c:396
ra8_err_t priv_utf16_to_utf8(const uint16_t *in, uint32_t units, char *out, uint32_t cap)
Convert UTF-16LE code units into a NUL-terminated UTF-8 name.
Definition ra8_fs_utf.c:499
static ra8_err_t internal_utf16_put(uint32_t cp, uint16_t *out, uint32_t cap, uint32_t *io_n)
Append cp to a UTF-16 buffer as one unit or as a surrogate pair.
Definition ra8_fs_utf.c:259
UTF-8 <-> UTF-16LE conversion and case folding for ra8_fs names.
@ k_utf_min_4byte
Smallest code point a 4-byte form may hold.
@ k_utf_lead3_tag
Tag of a three-byte lead byte.
@ k_utf_cont_shift
Payload width of one continuation byte.
@ k_utf_sur_shift
Payload width of one surrogate unit.
@ k_utf_code_max
Largest code point Unicode defines.
@ k_utf_len_1
Sequence length: plain ASCII.
@ k_utf_lead3_mask
Mask isolating a three-byte lead's tag.
@ k_utf_len_3
Sequence length: three bytes.
@ k_utf_sur_hi_first
First high (leading) surrogate unit.
@ k_utf_lead4_payload
Payload bits in a four-byte lead.
@ k_utf_lead2_payload
Payload bits in a two-byte lead.
@ k_utf_cont_tag
Tag every continuation byte carries.
@ k_utf_len_2
Sequence length: two bytes.
@ k_utf_lead4_tag
Tag of a four-byte lead byte.
@ k_utf_min_2byte
Smallest code point a 2-byte form may hold.
@ k_utf_lead4_mask
Mask isolating a four-byte lead's tag.
@ k_utf_sur_mask
Payload bits in one surrogate unit.
@ k_utf_cont_payload
Payload bits in a continuation byte.
@ k_utf_sur_lo_first
First low (trailing) surrogate unit.
@ k_utf_lead2_mask
Mask isolating a two-byte lead's tag.
@ k_utf_lead3_payload
Payload bits in a three-byte lead.
@ k_utf_ascii_max
Highest code point that is one UTF-8 byte.
@ k_utf_lead2_tag
Tag of a two-byte lead byte.
@ k_utf_sur_last
Last surrogate unit of either half.
@ k_utf_len_4
Sequence length: four bytes.
@ k_utf_cont_mask
Mask isolating a continuation byte's tag.
@ k_utf_min_3byte
Smallest code point a 3-byte form may hold.
@ k_utf8_max_per_unit
Worst-case UTF-8 bytes per UTF-16 unit.