Fcitx
cutf8.cpp
1 /*
2  * SPDX-FileCopyrightText: 2010-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "cutf8.h"
9 #include <cstdint>
10 #include <cstring>
11 #include "utf8.h"
12 
13 /** check utf8 character */
14 
15 #define CONT(i) fcitx::utf8::isContinuationByte(in[i])
16 #define VAL(i, s) ((in[i] & 0x3f) << s)
17 
18 #define UTF8_LENGTH(Char) \
19  ((Char) < 0x80 \
20  ? 1 \
21  : ((Char) < 0x800 \
22  ? 2 \
23  : ((Char) < 0x10000 \
24  ? 3 \
25  : ((Char) < 0x200000 ? 4 \
26  : ((Char) < 0x4000000 ? 5 : 6)))))
27 
28 #define UNICODE_VALID(Char) \
29  ((Char) < 0x110000 && (((Char) & 0xFFFFF800) != 0xD800))
30 
31 size_t fcitx_utf8_strlen(const char *s) {
32  size_t l = 0;
33 
34  while (*s) {
35  uint32_t chr;
36 
37  s = fcitx_utf8_get_char(s, &chr);
38  l++;
39  }
40 
41  return l;
42 }
43 
44 unsigned int fcitx_utf8_char_len(const char *in) {
45  if (!(in[0] & 0x80)) {
46  return 1;
47  }
48 
49  /* 2-byte, 0x80-0x7ff */
50  if ((in[0] & 0xe0) == 0xc0 && CONT(1)) {
51  return 2;
52  }
53 
54  /* 3-byte, 0x800-0xffff */
55  if ((in[0] & 0xf0) == 0xe0 && CONT(1) && CONT(2)) {
56  return 3;
57  }
58 
59  /* 4-byte, 0x10000-0x1FFFFF */
60  if ((in[0] & 0xf8) == 0xf0 && CONT(1) && CONT(2) && CONT(3)) {
61  return 4;
62  }
63 
64  /* 5-byte, 0x200000-0x3FFFFFF */
65  if ((in[0] & 0xfc) == 0xf8 && CONT(1) && CONT(2) && CONT(3) && CONT(4)) {
66  return 5;
67  }
68 
69  /* 6-byte, 0x400000-0x7FFFFFF */
70  if ((in[0] & 0xfe) == 0xfc && CONT(1) && CONT(2) && CONT(3) && CONT(4) &&
71  CONT(5)) {
72  return 6;
73  }
74 
75  return 1;
76 }
77 
78 int fcitx_ucs4_char_len(uint32_t c) {
79  if (c < 0x00080) {
80  return 1;
81  }
82  if (c < 0x00800) {
83  return 2;
84  }
85  if (c < 0x10000) {
86  return 3;
87  }
88  if (c < 0x200000) {
89  return 4;
90  // below is not in UCS4 but in 32bit int.
91  }
92  if (c < 0x8000000) {
93  return 5;
94  }
95  return 6;
96 }
97 
98 int fcitx_ucs4_to_utf8(uint32_t c, char *output) {
99  if (c < 0x00080) {
100  output[0] = (char)(c & 0xFF);
101  output[1] = '\0';
102  return 1;
103  }
104  if (c < 0x00800) {
105  output[0] = (char)(0xC0 + ((c >> 6) & 0x1F));
106  output[1] = (char)(0x80 + (c & 0x3F));
107  output[2] = '\0';
108  return 2;
109  }
110  if (c < 0x10000) {
111  output[0] = (char)(0xE0 + ((c >> 12) & 0x0F));
112  output[1] = (char)(0x80 + ((c >> 6) & 0x3F));
113  output[2] = (char)(0x80 + (c & 0x3F));
114  output[3] = '\0';
115  return 3;
116  }
117  if (c < 0x200000) {
118  output[0] = (char)(0xF0 + ((c >> 18) & 0x07));
119  output[1] = (char)(0x80 + ((c >> 12) & 0x3F));
120  output[2] = (char)(0x80 + ((c >> 6) & 0x3F));
121  output[3] = (char)(0x80 + (c & 0x3F));
122  output[4] = '\0';
123  return 4;
124  // below is not in UCS4 but in 32bit int.
125  }
126  if (c < 0x8000000) {
127  output[0] = (char)(0xF8 + ((c >> 24) & 0x03));
128  output[1] = (char)(0x80 + ((c >> 18) & 0x3F));
129  output[2] = (char)(0x80 + ((c >> 12) & 0x3F));
130  output[3] = (char)(0x80 + ((c >> 6) & 0x3F));
131  output[4] = (char)(0x80 + (c & 0x3F));
132  output[5] = '\0';
133  return 5;
134  }
135  output[0] = (char)(0xFC + ((c >> 30) & 0x01));
136  output[1] = (char)(0x80 + ((c >> 24) & 0x3F));
137  output[2] = (char)(0x80 + ((c >> 18) & 0x3F));
138  output[3] = (char)(0x80 + ((c >> 12) & 0x3F));
139  output[4] = (char)(0x80 + ((c >> 6) & 0x3F));
140  output[5] = (char)(0x80 + (c & 0x3F));
141  output[6] = '\0';
142  return 6;
143 }
144 
145 char *fcitx_utf8_get_char(const char *i, uint32_t *chr) {
146  const auto *in = reinterpret_cast<const unsigned char *>(i);
147  if (!(in[0] & 0x80)) {
148  *(chr) = *(in);
149  return (char *)in + 1;
150  }
151 
152  /* 2-byte, 0x80-0x7ff */
153  if ((in[0] & 0xe0) == 0xc0 && CONT(1)) {
154  *chr = ((in[0] & 0x1f) << 6) | VAL(1, 0);
155  return (char *)in + 2;
156  }
157 
158  /* 3-byte, 0x800-0xffff */
159  if ((in[0] & 0xf0) == 0xe0 && CONT(1) && CONT(2)) {
160  *chr = ((in[0] & 0xf) << 12) | VAL(1, 6) | VAL(2, 0);
161  return (char *)in + 3;
162  }
163 
164  /* 4-byte, 0x10000-0x1FFFFF */
165  if ((in[0] & 0xf8) == 0xf0 && CONT(1) && CONT(2) && CONT(3)) {
166  *chr = ((in[0] & 0x7) << 18) | VAL(1, 12) | VAL(2, 6) | VAL(3, 0);
167  return (char *)in + 4;
168  }
169 
170  /* 5-byte, 0x200000-0x3FFFFFF */
171  if ((in[0] & 0xfc) == 0xf8 && CONT(1) && CONT(2) && CONT(3) && CONT(4)) {
172  *chr = ((in[0] & 0x3) << 24) | VAL(1, 18) | VAL(2, 12) | VAL(3, 6) |
173  VAL(4, 0);
174  return (char *)in + 5;
175  }
176 
177  /* 6-byte, 0x400000-0x7FFFFFF */
178  if ((in[0] & 0xfe) == 0xfc && CONT(1) && CONT(2) && CONT(3) && CONT(4) &&
179  CONT(5)) {
180  *chr = ((in[0] & 0x1) << 30) | VAL(1, 24) | VAL(2, 18) | VAL(3, 12) |
181  VAL(4, 6) | VAL(5, 0);
182  return (char *)in + 6;
183  }
184 
185  *chr = *in;
186 
187  return (char *)in + 1;
188 }
189 
190 char *fcitx_utf8_get_nth_char(const char *s, uint32_t n) {
191  size_t l = 0;
192 
193  while (*s && l < n) {
194  uint32_t chr;
195 
196  s = fcitx_utf8_get_char(s, &chr);
197  l++;
198  }
199 
200  return (char *)s;
201 }
202 
203 static uint32_t fcitx_utf8_get_char_extended(const char *s, int max_len,
204  int *plen) {
205  const auto *p = reinterpret_cast<const unsigned char *>(s);
206  int i;
207  int len;
208  uint32_t wc = static_cast<unsigned char>(*p);
209 
210  if (wc < 0x80) {
211  if (plen) {
212  *plen = 1;
213  }
214  return wc;
215  }
216  if (wc < 0xc0) {
217  return (uint32_t)-1;
218  }
219  if (wc < 0xe0) {
220  len = 2;
221  wc &= 0x1f;
222  } else if (wc < 0xf0) {
223  len = 3;
224  wc &= 0x0f;
225  } else if (wc < 0xf8) {
226  len = 4;
227  wc &= 0x07;
228  } else if (wc < 0xfc) {
229  len = 5;
230  wc &= 0x03;
231  } else if (wc < 0xfe) {
232  len = 6;
233  wc &= 0x01;
234  } else {
235  return (uint32_t)-1;
236  }
237 
238  if (max_len >= 0 && len > max_len) {
239  for (i = 1; i < max_len; i++) {
240  if ((((unsigned char *)p)[i] & 0xc0) != 0x80) {
241  return (uint32_t)-1;
242  }
243  }
244 
245  return (uint32_t)-2;
246  }
247 
248  for (i = 1; i < len; ++i) {
249  uint32_t ch = ((unsigned char *)p)[i];
250 
251  if ((ch & 0xc0) != 0x80) {
252  if (ch) {
253  return (uint32_t)-1;
254  }
255  return (uint32_t)-2;
256  }
257 
258  wc <<= 6;
259 
260  wc |= (ch & 0x3f);
261  }
262 
263  if (UTF8_LENGTH(wc) != len) {
264  return (uint32_t)-1;
265  }
266 
267  if (plen) {
268  *plen = len;
269  }
270 
271  return wc;
272 }
273 
274 uint32_t fcitx_utf8_get_char_validated(const char *p, int max_len, int *plen) {
275  uint32_t result;
276 
277  if (max_len == 0) {
279  }
280 
281  int len;
282  result = fcitx_utf8_get_char_extended(p, max_len, &len);
283 
284  if (result & 0x80000000) {
285  return result;
286  }
287  if (!UNICODE_VALID(result)) {
289  }
290 
291  if (plen) {
292  *plen = len;
293  }
294  return result;
295 }
296 
297 bool fcitx_utf8_check_string(const char *s) {
298  while (*s) {
299  uint32_t chr;
300 
301  int len = 0;
302  chr = fcitx_utf8_get_char_validated(s, FCITX_UTF8_MAX_LENGTH, &len);
303  if (chr == fcitx::utf8::NOT_ENOUGH_SPACE ||
304  chr == fcitx::utf8::INVALID_CHAR) {
305  return false;
306  }
307 
308  s += len;
309  }
310 
311  return true;
312 }
313 
314 void fcitx_utf8_strncpy(char *str, const char *s, size_t byte) {
315  while (*s) {
316  uint32_t chr;
317 
318  const char *next = fcitx_utf8_get_char(s, &chr);
319  size_t diff = next - s;
320  if (byte < diff) {
321  break;
322  }
323 
324  memcpy(str, s, diff);
325  str += diff;
326  byte -= diff;
327  s = next;
328  }
329 
330  while (byte--) {
331  *str = '\0';
332  str++;
333  }
334 }
335 
336 size_t fcitx_utf8_strnlen_validated(const char *str, size_t byte) {
337  size_t len = 0;
338  while (byte && *str) {
339  int charLen = 0;
340  uint32_t chr = fcitx_utf8_get_char_validated(
341  str, (byte > FCITX_UTF8_MAX_LENGTH ? FCITX_UTF8_MAX_LENGTH : byte),
342  &charLen);
343  if (chr == fcitx::utf8::NOT_ENOUGH_SPACE ||
344  chr == fcitx::utf8::INVALID_CHAR) {
346  }
347  str += charLen;
348  byte -= charLen;
349  len++;
350  }
351  return len;
352 }
353 
354 size_t fcitx_utf8_strnlen(const char *str, size_t byte) {
355  size_t len = 0;
356  // if byte is zero no need to go further.
357  while (byte && *str) {
358  uint32_t chr;
359 
360  const char *next = fcitx_utf8_get_char(str, &chr);
361  size_t diff = next - str;
362  if (byte < diff) {
363  break;
364  }
365 
366  byte -= diff;
367  str = next;
368  len++;
369  }
370  return len;
371 }
void fcitx_utf8_strncpy(char *str, const char *s, size_t byte)
Copy most byte length, but keep utf8 valid.
Definition: cutf8.cpp:314
char * fcitx_utf8_get_nth_char(const char *s, uint32_t n)
Get the pointer to the nth character.
Definition: cutf8.cpp:190
C++ Utility functions for handling utf8 strings.
constexpr uint32_t INVALID_CHAR
Possible return value for getChar.
Definition: utf8.h:91
constexpr uint32_t NOT_ENOUGH_SPACE
Possible return value for getChar.
Definition: utf8.h:94
constexpr size_t INVALID_LENGTH
Possible return value of lengthValidated if the string is not valid.
Definition: utf8.h:53
uint32_t fcitx_utf8_get_char_validated(const char *p, int max_len, int *plen)
Get validated character.
Definition: cutf8.cpp:274
size_t fcitx_utf8_strlen(const char *s)
Get utf8 string length.
Definition: cutf8.cpp:31
C-style utf8 utility functions.
unsigned int fcitx_utf8_char_len(const char *in)
Get the number of bytes of next character.
Definition: cutf8.cpp:44
size_t fcitx_utf8_strnlen_validated(const char *str, size_t byte)
Count most byte length, utf8 string length and validates the string.
Definition: cutf8.cpp:336
size_t fcitx_utf8_strnlen(const char *str, size_t byte)
Count most byte length, utf8 string length.
Definition: cutf8.cpp:354
int fcitx_ucs4_to_utf8(uint32_t c, char *output)
Convert ucs4 char to utf8, need to have enough memory for it.
Definition: cutf8.cpp:98
int fcitx_ucs4_char_len(uint32_t c)
Return the utf8 bytes of a UCS4 char.
Definition: cutf8.cpp:78
char * fcitx_utf8_get_char(const char *i, uint32_t *chr)
Get UCS-4 char in the utf8 string.
Definition: cutf8.cpp:145
bool fcitx_utf8_check_string(const char *s)
Check if the string is valid utf8 string.
Definition: cutf8.cpp:297