==> src/utf8.c <==1/* UTF-8 with Rust's error handling. */
2#include "tui/utf8.h"
3
4#include <stdlib.h>
5#include <string.h>
6
7uint32_t tui_utf8_next(const char *str, size_t len, size_t *i) {
8 const unsigned char *s = (const unsigned char *)str;
9 unsigned char b = s[*i];
10 if (b < 0x80) {
11 (*i)++;
12 return b;
13 }
14 int need;
15 unsigned char lo = 0x80, hi = 0xBF;
16 uint32_t cp;
17 if (b >= 0xC2 && b <= 0xDF) {
18 need = 1;
19 cp = b & 0x1F;
20 } else if (b >= 0xE0 && b <= 0xEF) {
21 need = 2;
22 cp = b & 0x0F;
23 if (b == 0xE0) lo = 0xA0;
24 if (b == 0xED) hi = 0x9F;
25 } else if (b >= 0xF0 && b <= 0xF4) {
26 need = 3;
27 cp = b & 0x07;
28 if (b == 0xF0) lo = 0x90;
29 if (b == 0xF4) hi = 0x8F;
30 } else {
31 (*i)++;
32 return TUI_UTF8_REPLACEMENT;
33 }
34 size_t j = *i + 1;
35 for (int k = 0; k < need; k++, j++) {
36 unsigned char c = j < len ? s[j] : 0;
37 if (j >= len || c < lo || c > hi) {
38 *i = j; /* the maximal subpart ends before the offending byte */
39 return TUI_UTF8_REPLACEMENT;
40 }
41 cp = cp << 6 | (c & 0x3F);
42 lo = 0x80;
43 hi = 0xBF;
44 }
45 *i = j;
46 return cp;
47}
48
49size_t tui_utf8_encode(uint32_t cp, char out[4]) {
50 if (cp < 0x80) {
51 out[0] = (char)cp;
52 return 1;
53 }
54 if (cp < 0x800) {
55 out[0] = (char)(0xC0 | cp >> 6);
56 out[1] = (char)(0x80 | (cp & 0x3F));
57 return 2;
58 }
59 if (cp < 0x10000) {
60 out[0] = (char)(0xE0 | cp >> 12);
61 out[1] = (char)(0x80 | (cp >> 6 & 0x3F));
62 out[2] = (char)(0x80 | (cp & 0x3F));
63 return 3;
64 }
65 out[0] = (char)(0xF0 | cp >> 18);
66 out[1] = (char)(0x80 | (cp >> 12 & 0x3F));
67 out[2] = (char)(0x80 | (cp >> 6 & 0x3F));
68 out[3] = (char)(0x80 | (cp & 0x3F));
69 return 4;
70}
71
72bool tui_utf8_valid(const char *s, size_t len) {
73 for (size_t i = 0; i < len;) {
74 size_t at = i;
75 uint32_t cp = tui_utf8_next(s, len, &i);
76 if (cp == TUI_UTF8_REPLACEMENT && !(i - at == 3 && (unsigned char)s[at] == 0xEF &&
77 (unsigned char)s[at + 1] == 0xBF && (unsigned char)s[at + 2] == 0xBD))
78 return false;
79 }
80 return true;
81}
82
83char *tui_utf8_lossy(const void *bytes, size_t len, size_t *out_len) {
84 const char *s = bytes;
85 /* Each byte becomes at most 3 (a lone bad byte -> U+FFFD). */
86 char *out = malloc(len * 3 + 1);
87 if (!out) abort();
88 size_t n = 0;
89 for (size_t i = 0; i < len;) {
90 size_t at = i;
91 uint32_t cp = tui_utf8_next(s, len, &i);
92 if (cp == TUI_UTF8_REPLACEMENT || cp >= 0x80) {
93 if (cp != TUI_UTF8_REPLACEMENT || i - at != 3 || memcmp(s + at, "\xEF\xBF\xBD", 3) != 0) {
94 n += tui_utf8_encode(cp, out + n);
95 continue;
96 }
97 }
98 memcpy(out + n, s + at, i - at);
99 n += i - at;
100 }
101 out[n] = 0;
102 if (out_len) *out_len = n;
103 return out;
104}
105
106size_t tui_utf8_count(const char *s, size_t len) {
107 size_t n = 0;
108 for (size_t i = 0; i < len; i++) n += ((unsigned char)s[i] & 0xC0) != 0x80;
109 return n;
110}