skip to content
==> 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}