==> tests/utf8.c <==1/* Decoding and repair against Rust's String::from_utf8_lossy (the strings below are what Rust
2 * printed for the same bytes, as code points). */
3#include "mesh/check.h"
4#include "tui/utf8.h"
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10static const char *cps(const char *s, size_t len) {
11 static char buf[512];
12 size_t n = 0;
13 buf[0] = 0;
14 for (size_t i = 0; i < len;) {
15 uint32_t cp = tui_utf8_next(s, len, &i);
16 n += (size_t)snprintf(buf + n, sizeof buf - n, n ? " %x" : "%x", cp);
17 }
18 return buf;
19}
20
21int main(void) {
22 struct {
23 const char *in;
24 size_t len;
25 const char *want;
26 } cases[] = {
27 {"abc", 3, "61 62 63"},
28 {"\xC3\xA9", 2, "e9"},
29 {"\xE2\x86\x92", 3, "2192"},
30 {"\xF0\x9F\x91\x8D", 4, "1f44d"},
31 {"\x80", 1, "fffd"},
32 {"\xC0\xAF", 2, "fffd fffd"},
33 {"\xE0\x80\x80", 3, "fffd fffd fffd"},
34 {"\xED\xA0\x80", 3, "fffd fffd fffd"},
35 {"\xF0\x9F\x91", 3, "fffd"},
36 {"\xF0\x9F\x91" "A", 4, "fffd 41"},
37 {"\xE2\x86" "x", 3, "fffd 78"},
38 {"\xF4\x90\x80\x80", 4, "fffd fffd fffd fffd"},
39 {"\xF5", 1, "fffd"},
40 {"a\xFF" "b", 3, "61 fffd 62"},
41 };
42 for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) CHECK_S(cps(cases[i].in, cases[i].len), cases[i].want);
43 size_t n;
44 char *r = tui_utf8_lossy("a\xF0\x9F\x91" "b\xC3", 6, &n);
45 CHECK_S(r, "a\xEF\xBF\xBD" "b\xEF\xBF\xBD");
46 CHECK_U(n, 8);
47 free(r);
48 r = tui_utf8_lossy("ok \xEF\xBF\xBD", 6, NULL);
49 CHECK_S(r, "ok \xEF\xBF\xBD");
50 free(r);
51 char enc[4];
52 CHECK_U(tui_utf8_encode(0x1F44D, enc), 4);
53 CHECK(memcmp(enc, "\xF0\x9F\x91\x8D", 4) == 0);
54 CHECK_U(tui_utf8_encode(0xB5, enc), 2);
55 CHECK(tui_utf8_valid("→ ok", strlen("→ ok")));
56 CHECK(!tui_utf8_valid("\xC3", 1));
57 CHECK_U(tui_utf8_count("→1 µs", strlen("→1 µs")), 5);
58 return mesh_check_done();
59}