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