Branch data Line data Source code
1 : : /* gutf8.c - Operations on UTF-8 strings.
2 : : *
3 : : * Copyright (C) 1999 Tom Tromey
4 : : * Copyright (C) 2000, 2015-2022 Red Hat, Inc.
5 : : * Copyright (C) 2022-2023 David Rheinsberg
6 : : *
7 : : * SPDX-License-Identifier: LGPL-2.1-or-later
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General Public
20 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <stdlib.h>
26 : : #ifdef HAVE_CODESET
27 : : #include <langinfo.h>
28 : : #endif
29 : : #include <string.h>
30 : : #include <stdbool.h>
31 : :
32 : : #ifdef G_PLATFORM_WIN32
33 : : #include <stdio.h>
34 : : #include <windows.h>
35 : : #endif
36 : :
37 : : #include "gconvert.h"
38 : : #include "ghash.h"
39 : : #include "gstrfuncs.h"
40 : : #include "gtestutils.h"
41 : : #include "gtypes.h"
42 : : #include "gthread.h"
43 : : #include "glibintl.h"
44 : : #include "gvalgrind.h"
45 : : #include "gunicodeprivate.h"
46 : :
47 : : #define UTF8_COMPUTE(Char, Mask, Len) \
48 : : if (Char < 128) \
49 : : { \
50 : : Len = 1; \
51 : : Mask = 0x7f; \
52 : : } \
53 : : else if ((Char & 0xe0) == 0xc0) \
54 : : { \
55 : : Len = 2; \
56 : : Mask = 0x1f; \
57 : : } \
58 : : else if ((Char & 0xf0) == 0xe0) \
59 : : { \
60 : : Len = 3; \
61 : : Mask = 0x0f; \
62 : : } \
63 : : else if ((Char & 0xf8) == 0xf0) \
64 : : { \
65 : : Len = 4; \
66 : : Mask = 0x07; \
67 : : } \
68 : : else if ((Char & 0xfc) == 0xf8) \
69 : : { \
70 : : Len = 5; \
71 : : Mask = 0x03; \
72 : : } \
73 : : else if ((Char & 0xfe) == 0xfc) \
74 : : { \
75 : : Len = 6; \
76 : : Mask = 0x01; \
77 : : } \
78 : : else \
79 : : Len = -1;
80 : :
81 : : #define UTF8_LENGTH(Char) \
82 : : ((Char) < 0x80 ? 1 : \
83 : : ((Char) < 0x800 ? 2 : \
84 : : ((Char) < 0x10000 ? 3 : \
85 : : ((Char) < 0x200000 ? 4 : \
86 : : ((Char) < 0x4000000 ? 5 : 6)))))
87 : :
88 : :
89 : : #define UTF8_GET(Result, Chars, Count, Mask, Len) \
90 : : (Result) = (Chars)[0] & (Mask); \
91 : : for ((Count) = 1; (Count) < (Len); ++(Count)) \
92 : : { \
93 : : if (((Chars)[(Count)] & 0xc0) != 0x80) \
94 : : { \
95 : : (Result) = -1; \
96 : : break; \
97 : : } \
98 : : (Result) <<= 6; \
99 : : (Result) |= ((Chars)[(Count)] & 0x3f); \
100 : : }
101 : :
102 : : /*
103 : : * Check whether a Unicode (5.2) char is in a valid range.
104 : : *
105 : : * The first check comes from the Unicode guarantee to never encode
106 : : * a point above 0x0010ffff, since UTF-16 couldn't represent it.
107 : : *
108 : : * The second check covers surrogate pairs (category Cs).
109 : : *
110 : : * @param Char the character
111 : : */
112 : : #define UNICODE_VALID(Char) \
113 : : ((Char) < 0x110000 && \
114 : : (((Char) & 0xFFFFF800) != 0xD800))
115 : :
116 : :
117 : : static const gchar utf8_skip_data[256] = {
118 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
119 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
120 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
121 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
122 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
123 : : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
124 : : 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
125 : : 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
126 : : };
127 : :
128 : : const gchar * const g_utf8_skip = utf8_skip_data;
129 : :
130 : : /**
131 : : * g_utf8_find_prev_char:
132 : : * @str: pointer to the beginning of a UTF-8 encoded string
133 : : * @p: pointer to some position within @str
134 : : *
135 : : * Given a position @p with a UTF-8 encoded string @str, find the start
136 : : * of the previous UTF-8 character starting before @p. Returns `NULL` if no
137 : : * UTF-8 characters are present in @str before @p.
138 : : *
139 : : * @p does not have to be at the beginning of a UTF-8 character. No check
140 : : * is made to see if the character found is actually valid other than
141 : : * it starts with an appropriate byte.
142 : : *
143 : : * Returns: (transfer none) (nullable): a pointer to the found character
144 : : */
145 : : gchar *
146 : 30 : g_utf8_find_prev_char (const gchar *str,
147 : : const gchar *p)
148 : : {
149 : 54 : while (p > str)
150 : : {
151 : 51 : --p;
152 : 51 : if ((*p & 0xc0) != 0x80)
153 : 27 : return (gchar *)p;
154 : : }
155 : 3 : return NULL;
156 : : }
157 : :
158 : : /**
159 : : * g_utf8_find_next_char:
160 : : * @p: a pointer to a position within a UTF-8 encoded string
161 : : * @end: (nullable): a pointer to the byte following the end of the string,
162 : : * or `NULL` to indicate that the string is nul-terminated
163 : : *
164 : : * Finds the start of the next UTF-8 character in the string after @p.
165 : : *
166 : : * @p does not have to be at the beginning of a UTF-8 character. No check
167 : : * is made to see if the character found is actually valid other than
168 : : * it starts with an appropriate byte.
169 : : *
170 : : * If @end is `NULL`, the return value will never be `NULL`: if the end of the
171 : : * string is reached, a pointer to the terminating nul byte is returned. If
172 : : * @end is non-`NULL`, the return value will be `NULL` if the end of the string
173 : : * is reached.
174 : : *
175 : : * Returns: (transfer none) (nullable): a pointer to the found character or `NULL` if @end is
176 : : * set and is reached
177 : : */
178 : : gchar *
179 : 844828 : g_utf8_find_next_char (const gchar *p,
180 : : const gchar *end)
181 : : {
182 : 844828 : if (end)
183 : : {
184 : 759812 : for (++p; p < end && (*p & 0xc0) == 0x80; ++p)
185 : : ;
186 : 759804 : return (p >= end) ? NULL : (gchar *)p;
187 : : }
188 : : else
189 : : {
190 : 85038 : for (++p; (*p & 0xc0) == 0x80; ++p)
191 : : ;
192 : 85024 : return (gchar *)p;
193 : : }
194 : : }
195 : :
196 : : /**
197 : : * g_utf8_prev_char:
198 : : * @p: a pointer to a position within a UTF-8 encoded string
199 : : *
200 : : * Finds the previous UTF-8 character in the string before @p.
201 : : *
202 : : * @p does not have to be at the beginning of a UTF-8 character. No check
203 : : * is made to see if the character found is actually valid other than
204 : : * it starts with an appropriate byte. If @p might be the first
205 : : * character of the string, you must use [func@GLib.utf8_find_prev_char]
206 : : * instead.
207 : : *
208 : : * Returns: (transfer none) (not nullable): a pointer to the found character
209 : : */
210 : : gchar *
211 : 282 : g_utf8_prev_char (const gchar *p)
212 : : {
213 : : while (TRUE)
214 : : {
215 : 465 : p--;
216 : 465 : if ((*p & 0xc0) != 0x80)
217 : 282 : return (gchar *)p;
218 : : }
219 : : }
220 : :
221 : : /**
222 : : * g_utf8_strlen:
223 : : * @p: pointer to the start of a UTF-8 encoded string
224 : : * @max: the maximum number of bytes to examine. If @max
225 : : * is less than 0, then the string is assumed to be
226 : : * nul-terminated. If @max is 0, @p will not be examined and
227 : : * may be `NULL`. If @max is greater than 0, up to @max
228 : : * bytes are examined
229 : : *
230 : : * Computes the length of the string in characters, not including
231 : : * the terminating nul character. If the @max’th byte falls in the
232 : : * middle of a character, the last (partial) character is not counted.
233 : : *
234 : : * Returns: the length of the string in characters
235 : : */
236 : : glong
237 : 63522 : g_utf8_strlen (const gchar *p,
238 : : gssize max)
239 : : {
240 : 63522 : glong len = 0;
241 : 63522 : const gchar *start = p;
242 : 63522 : g_return_val_if_fail (p != NULL || max == 0, 0);
243 : :
244 : 63522 : if (max < 0)
245 : : {
246 : 184859 : while (*p)
247 : : {
248 : 121362 : p = g_utf8_next_char (p);
249 : 121362 : ++len;
250 : : }
251 : : }
252 : : else
253 : : {
254 : 25 : if (max == 0 || !*p)
255 : 3 : return 0;
256 : :
257 : 22 : p = g_utf8_next_char (p);
258 : :
259 : 1579 : while (p - start < max && *p)
260 : : {
261 : 1557 : ++len;
262 : 1557 : p = g_utf8_next_char (p);
263 : : }
264 : :
265 : : /* only do the last len increment if we got a complete
266 : : * char (don't count partial chars)
267 : : */
268 : 22 : if (p - start <= max)
269 : 16 : ++len;
270 : : }
271 : :
272 : 63519 : return len;
273 : : }
274 : :
275 : : /**
276 : : * g_utf8_substring:
277 : : * @str: a UTF-8 encoded string
278 : : * @start_pos: a character offset within @str
279 : : * @end_pos: another character offset within @str,
280 : : * or `-1` to indicate the end of the string
281 : : *
282 : : * Copies a substring out of a UTF-8 encoded string.
283 : : * The substring will contain @end_pos - @start_pos characters.
284 : : *
285 : : * Since GLib 2.72, `-1` can be passed to @end_pos to indicate the
286 : : * end of the string.
287 : : *
288 : : * Returns: (transfer full): a newly allocated copy of the requested
289 : : * substring. Free with [func@GLib.free] when no longer needed.
290 : : *
291 : : * Since: 2.30
292 : : */
293 : : gchar *
294 : 5 : g_utf8_substring (const gchar *str,
295 : : glong start_pos,
296 : : glong end_pos)
297 : : {
298 : : gchar *start, *end, *out;
299 : :
300 : 5 : g_return_val_if_fail (end_pos >= start_pos || end_pos == -1, NULL);
301 : :
302 : 5 : start = g_utf8_offset_to_pointer (str, start_pos);
303 : :
304 : 5 : if (end_pos == -1)
305 : : {
306 : 1 : glong length = g_utf8_strlen (start, -1);
307 : 1 : end = g_utf8_offset_to_pointer (start, length);
308 : : }
309 : : else
310 : : {
311 : 4 : end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
312 : : }
313 : :
314 : 5 : out = g_malloc (end - start + 1);
315 : 5 : memcpy (out, start, end - start);
316 : 5 : out[end - start] = 0;
317 : :
318 : 5 : return out;
319 : : }
320 : :
321 : : /**
322 : : * g_utf8_get_char:
323 : : * @p: a pointer to Unicode character encoded as UTF-8
324 : : *
325 : : * Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
326 : : *
327 : : * If @p does not point to a valid UTF-8 encoded character, results
328 : : * are undefined. If you are not sure that the bytes are complete
329 : : * valid Unicode characters, you should use [func@GLib.utf8_get_char_validated]
330 : : * instead.
331 : : *
332 : : * Returns: the resulting character
333 : : */
334 : : gunichar
335 : 15069808 : g_utf8_get_char (const gchar *p)
336 : : {
337 : 15069808 : int i, mask = 0, len;
338 : : gunichar result;
339 : 15069808 : unsigned char c = (unsigned char) *p;
340 : :
341 : 15069808 : UTF8_COMPUTE (c, mask, len);
342 : 15069808 : if (len == -1)
343 : 16 : return (gunichar)-1;
344 : 17869475 : UTF8_GET (result, p, i, mask, len);
345 : :
346 : 15069792 : return result;
347 : : }
348 : :
349 : : /**
350 : : * g_utf8_offset_to_pointer:
351 : : * @str: a UTF-8 encoded string
352 : : * @offset: a character offset within @str
353 : : *
354 : : * Converts from an integer character offset to a pointer to a position
355 : : * within the string.
356 : : *
357 : : * Since 2.10, this function allows to pass a negative @offset to
358 : : * step backwards. It is usually worth stepping backwards from the end
359 : : * instead of forwards if @offset is in the last fourth of the string,
360 : : * since moving forward is about 3 times faster than moving backward.
361 : : *
362 : : * Note that this function doesn’t abort when reaching the end of @str.
363 : : * Therefore you should be sure that @offset is within string boundaries
364 : : * before calling that function. Call [func@GLib.utf8_strlen] when unsure.
365 : : * This limitation exists as this function is called frequently during
366 : : * text rendering and therefore has to be as fast as possible.
367 : : *
368 : : * Returns: (transfer none): the resulting pointer
369 : : */
370 : : gchar *
371 : 580726 : g_utf8_offset_to_pointer (const gchar *str,
372 : : glong offset)
373 : : {
374 : 580726 : const gchar *s = str;
375 : :
376 : 580726 : if (offset > 0)
377 : 74032020 : while (offset--)
378 : 73742005 : s = g_utf8_next_char (s);
379 : : else
380 : : {
381 : : const char *s1;
382 : :
383 : : /* This nice technique for fast backwards stepping
384 : : * through a UTF-8 string was dubbed "stutter stepping"
385 : : * by its inventor, Larry Ewing.
386 : : */
387 : 954612 : while (offset)
388 : : {
389 : 663901 : s1 = s;
390 : 663901 : s += offset;
391 : 764185 : while ((*s & 0xc0) == 0x80)
392 : 100284 : s--;
393 : :
394 : 663901 : offset += g_utf8_pointer_to_offset (s, s1);
395 : : }
396 : : }
397 : :
398 : 580726 : return (gchar *)s;
399 : : }
400 : :
401 : : /**
402 : : * g_utf8_pointer_to_offset:
403 : : * @str: a UTF-8 encoded string
404 : : * @pos: a pointer to a position within @str
405 : : *
406 : : * Converts from a pointer to position within a string to an integer
407 : : * character offset.
408 : : *
409 : : * Since 2.10, this function allows @pos to be before @str, and returns
410 : : * a negative offset in this case.
411 : : *
412 : : * Returns: the resulting character offset
413 : : */
414 : : glong
415 : 1534545 : g_utf8_pointer_to_offset (const gchar *str,
416 : : const gchar *pos)
417 : : {
418 : 1534545 : const gchar *s = str;
419 : 1534545 : glong offset = 0;
420 : :
421 : 1534545 : if (pos < str)
422 : 289941 : offset = - g_utf8_pointer_to_offset (pos, str);
423 : : else
424 : 222470156 : while (s < pos)
425 : : {
426 : 221225552 : s = g_utf8_next_char (s);
427 : 221225552 : offset++;
428 : : }
429 : :
430 : 1534545 : return offset;
431 : : }
432 : :
433 : :
434 : : /**
435 : : * g_utf8_strncpy:
436 : : * @dest: (transfer none): buffer to fill with characters from @src
437 : : * @src: UTF-8 encoded string
438 : : * @n: character count
439 : : *
440 : : * Like the standard C [`strncpy()`](man:strncpy) function, but copies a given
441 : : * number of characters instead of a given number of bytes.
442 : : *
443 : : * The @src string must be valid UTF-8 encoded text. (Use
444 : : * [func@GLib.utf8_validate] on all text before trying to use UTF-8 utility
445 : : * functions with it.)
446 : : *
447 : : * Note you must ensure @dest is at least 4 * @n + 1 to fit the
448 : : * largest possible UTF-8 characters
449 : : *
450 : : * Returns: (transfer none): @dest
451 : : */
452 : : gchar *
453 : 8 : g_utf8_strncpy (gchar *dest,
454 : : const gchar *src,
455 : : gsize n)
456 : : {
457 : 8 : const gchar *s = src;
458 : 35 : while (n && *s)
459 : : {
460 : 27 : s = g_utf8_next_char(s);
461 : 27 : n--;
462 : : }
463 : 8 : strncpy(dest, src, s - src);
464 : 8 : dest[s - src] = 0;
465 : 8 : return dest;
466 : : }
467 : :
468 : : /**
469 : : * g_utf8_truncate_middle:
470 : : * @string: (transfer none): a nul-terminated UTF-8 encoded string
471 : : * @truncate_length: the new size of @string, in characters, including the ellipsis character
472 : : *
473 : : * Cuts off the middle of the string, preserving half of @truncate_length
474 : : * characters at the beginning and half at the end.
475 : : *
476 : : * If @string is already short enough, this returns a copy of @string.
477 : : * If @truncate_length is `0`, an empty string is returned.
478 : : *
479 : : * Returns: (transfer full): a newly-allocated copy of @string ellipsized in the middle
480 : : *
481 : : * Since: 2.78
482 : : */
483 : : gchar *
484 : 34 : g_utf8_truncate_middle (const gchar *string,
485 : : gsize truncate_length)
486 : : {
487 : 34 : const gchar *ellipsis = "…";
488 : 34 : const gsize ellipsis_bytes = strlen (ellipsis);
489 : :
490 : : gsize length;
491 : : gsize left_substring_length;
492 : : gchar *left_substring_end;
493 : : gchar *right_substring_begin;
494 : : gchar *right_substring_end;
495 : : gsize left_bytes;
496 : : gsize right_bytes;
497 : : gchar *result;
498 : :
499 : 34 : g_return_val_if_fail (string != NULL, NULL);
500 : :
501 : 34 : length = g_utf8_strlen (string, -1);
502 : : /* Current string already smaller than requested length */
503 : 34 : if (length <= truncate_length)
504 : 8 : return g_strdup (string);
505 : 26 : if (truncate_length == 0)
506 : 2 : return g_strdup ("");
507 : :
508 : : /* Find substrings to keep, ignore ellipsis character for that */
509 : 24 : truncate_length -= 1;
510 : :
511 : 24 : left_substring_length = truncate_length / 2;
512 : :
513 : 24 : left_substring_end = g_utf8_offset_to_pointer (string, left_substring_length);
514 : 24 : right_substring_begin = g_utf8_offset_to_pointer (left_substring_end,
515 : 24 : length - truncate_length);
516 : 24 : right_substring_end = g_utf8_offset_to_pointer (right_substring_begin,
517 : 24 : truncate_length - left_substring_length);
518 : :
519 : 24 : g_assert (*right_substring_end == '\0');
520 : :
521 : 24 : left_bytes = left_substring_end - string;
522 : 24 : right_bytes = right_substring_end - right_substring_begin;
523 : :
524 : 24 : result = g_malloc (left_bytes + ellipsis_bytes + right_bytes + 1);
525 : :
526 : 24 : strncpy (result, string, left_bytes);
527 : 24 : memcpy (result + left_bytes, ellipsis, ellipsis_bytes);
528 : 24 : strncpy (result + left_bytes + ellipsis_bytes, right_substring_begin, right_bytes);
529 : 24 : result[left_bytes + ellipsis_bytes + right_bytes] = '\0';
530 : :
531 : 24 : return result;
532 : : }
533 : :
534 : : /* unicode_strchr */
535 : :
536 : : /**
537 : : * g_unichar_to_utf8:
538 : : * @c: a Unicode character code
539 : : * @outbuf: (out caller-allocates) (optional): output buffer, must have at
540 : : * least 6 bytes of space. If `NULL`, the length will be computed and
541 : : * returned and nothing will be written to @outbuf.
542 : : *
543 : : * Converts a single character to UTF-8.
544 : : *
545 : : * Returns: number of bytes written, guaranteed to be in the range [1, 6]
546 : : */
547 : : int
548 : 982109 : g_unichar_to_utf8 (gunichar c,
549 : : gchar *outbuf)
550 : : {
551 : : /* If this gets modified, also update the copy in g_string_insert_unichar() */
552 : 982109 : size_t len = 0;
553 : : char first;
554 : : size_t i;
555 : :
556 : 982109 : if (c < 0x80)
557 : : {
558 : 206627 : first = 0;
559 : 206627 : len = 1;
560 : : }
561 : 775482 : else if (c < 0x800)
562 : : {
563 : 183159 : first = 0xc0;
564 : 183159 : len = 2;
565 : : }
566 : 592323 : else if (c < 0x10000)
567 : : {
568 : 560108 : first = 0xe0;
569 : 560108 : len = 3;
570 : : }
571 : 32215 : else if (c < 0x200000)
572 : : {
573 : 32211 : first = 0xf0;
574 : 32211 : len = 4;
575 : : }
576 : 4 : else if (c < 0x4000000)
577 : : {
578 : 2 : first = 0xf8;
579 : 2 : len = 5;
580 : : }
581 : : else
582 : : {
583 : 2 : first = 0xfc;
584 : 2 : len = 6;
585 : : }
586 : :
587 : 982109 : if (outbuf)
588 : : {
589 : 2369097 : for (i = len - 1; i > 0; --i)
590 : : {
591 : 1391272 : outbuf[i] = (c & 0x3f) | 0x80;
592 : 1391272 : c >>= 6;
593 : : }
594 : 977825 : outbuf[0] = c | first;
595 : : }
596 : :
597 : 982109 : return len;
598 : : }
599 : :
600 : : /**
601 : : * g_utf8_strchr:
602 : : * @p: a nul-terminated UTF-8 encoded string
603 : : * @len: the maximum length of @p
604 : : * @c: a Unicode character
605 : : *
606 : : * Finds the leftmost occurrence of the given Unicode character
607 : : * in a UTF-8 encoded string, while limiting the search to @len bytes.
608 : : *
609 : : * If @len is `-1`, allow unbounded search.
610 : : *
611 : : * Returns: (transfer none) (nullable): `NULL` if the string does not contain
612 : : * the character, otherwise, a pointer to the start of the leftmost occurrence
613 : : * of the character in the string.
614 : : */
615 : : gchar *
616 : 58005 : g_utf8_strchr (const char *p,
617 : : gssize len,
618 : : gunichar c)
619 : : {
620 : : gchar ch[10];
621 : :
622 : 58005 : gint charlen = g_unichar_to_utf8 (c, ch);
623 : 58005 : ch[charlen] = '\0';
624 : :
625 : 58005 : return g_strstr_len (p, len, ch);
626 : : }
627 : :
628 : :
629 : : /**
630 : : * g_utf8_strrchr:
631 : : * @p: a nul-terminated UTF-8 encoded string
632 : : * @len: the maximum length of @p
633 : : * @c: a Unicode character
634 : : *
635 : : * Find the rightmost occurrence of the given Unicode character
636 : : * in a UTF-8 encoded string, while limiting the search to @len bytes.
637 : : *
638 : : * If @len is `-1`, allow unbounded search.
639 : : *
640 : : * Returns: (transfer none) (nullable): `NULL` if the string does not contain
641 : : * the character, otherwise, a pointer to the start of the rightmost
642 : : * occurrence of the character in the string.
643 : : */
644 : : gchar *
645 : 5 : g_utf8_strrchr (const char *p,
646 : : gssize len,
647 : : gunichar c)
648 : : {
649 : : gchar ch[10];
650 : :
651 : 5 : gint charlen = g_unichar_to_utf8 (c, ch);
652 : 5 : ch[charlen] = '\0';
653 : :
654 : 5 : return g_strrstr_len (p, len, ch);
655 : : }
656 : :
657 : :
658 : : /* Like g_utf8_get_char, but take a maximum length
659 : : * and return (gunichar)-2 on incomplete trailing character;
660 : : * also check for malformed or overlong sequences
661 : : * and return (gunichar)-1 in this case.
662 : : */
663 : : static inline gunichar
664 : 2612413 : g_utf8_get_char_extended (const gchar *p,
665 : : gssize max_len)
666 : : {
667 : : gsize i, len;
668 : : gunichar min_code;
669 : 2612413 : gunichar wc = (guchar) *p;
670 : 2612413 : const gunichar partial_sequence = (gunichar) -2;
671 : 2612413 : const gunichar malformed_sequence = (gunichar) -1;
672 : :
673 : 2612413 : if (wc < 0x80)
674 : : {
675 : 2384622 : return wc;
676 : : }
677 : 227791 : else if (G_UNLIKELY (wc < 0xc0))
678 : : {
679 : 0 : return malformed_sequence;
680 : : }
681 : 227791 : else if (wc < 0xe0)
682 : : {
683 : 41859 : len = 2;
684 : 41859 : wc &= 0x1f;
685 : 41859 : min_code = 1 << 7;
686 : : }
687 : 185932 : else if (wc < 0xf0)
688 : : {
689 : 176041 : len = 3;
690 : 176041 : wc &= 0x0f;
691 : 176041 : min_code = 1 << 11;
692 : : }
693 : 9891 : else if (wc < 0xf8)
694 : : {
695 : 9854 : len = 4;
696 : 9854 : wc &= 0x07;
697 : 9854 : min_code = 1 << 16;
698 : : }
699 : 37 : else if (wc < 0xfc)
700 : : {
701 : 6 : len = 5;
702 : 6 : wc &= 0x03;
703 : 6 : min_code = 1 << 21;
704 : : }
705 : 31 : else if (wc < 0xfe)
706 : : {
707 : 31 : len = 6;
708 : 31 : wc &= 0x01;
709 : 31 : min_code = 1 << 26;
710 : : }
711 : : else
712 : : {
713 : 0 : return malformed_sequence;
714 : : }
715 : :
716 : 227791 : if (G_UNLIKELY (max_len >= 0 && len > (gsize) max_len))
717 : : {
718 : 624 : for (i = 1; i < (gsize) max_len; i++)
719 : : {
720 : 187 : if ((((guchar *)p)[i] & 0xc0) != 0x80)
721 : 1 : return malformed_sequence;
722 : : }
723 : 437 : return partial_sequence;
724 : : }
725 : :
726 : 649962 : for (i = 1; i < len; ++i)
727 : : {
728 : 422679 : gunichar ch = ((guchar *)p)[i];
729 : :
730 : 422679 : if (G_UNLIKELY ((ch & 0xc0) != 0x80))
731 : : {
732 : 70 : if (ch)
733 : 37 : return malformed_sequence;
734 : : else
735 : 33 : return partial_sequence;
736 : : }
737 : :
738 : 422609 : wc <<= 6;
739 : 422609 : wc |= (ch & 0x3f);
740 : : }
741 : :
742 : 227283 : if (G_UNLIKELY (wc < min_code))
743 : 0 : return malformed_sequence;
744 : :
745 : 227283 : return wc;
746 : : }
747 : :
748 : : /**
749 : : * g_utf8_get_char_validated:
750 : : * @p: a pointer to Unicode character encoded as UTF-8
751 : : * @max_len: the maximum number of bytes to read, or `-1` if @p is nul-terminated
752 : : *
753 : : * Convert a sequence of bytes encoded as UTF-8 to a Unicode character.
754 : : *
755 : : * This function checks for incomplete characters, for invalid characters
756 : : * such as characters that are out of the range of Unicode, and for
757 : : * overlong encodings of valid characters.
758 : : *
759 : : * Note that [func@GLib.utf8_get_char_validated] returns `(gunichar)-2` if
760 : : * @max_len is positive and any of the bytes in the first UTF-8 character
761 : : * sequence are nul.
762 : : *
763 : : * Returns: the resulting character. If @p points to a partial
764 : : * sequence at the end of a string that could begin a valid
765 : : * character (or if @max_len is zero), returns `(gunichar)-2`;
766 : : * otherwise, if @p does not point to a valid UTF-8 encoded
767 : : * Unicode character, returns `(gunichar)-1`.
768 : : */
769 : : gunichar
770 : 2607294 : g_utf8_get_char_validated (const gchar *p,
771 : : gssize max_len)
772 : : {
773 : : gunichar result;
774 : :
775 : 2607294 : if (max_len == 0)
776 : 1 : return (gunichar)-2;
777 : :
778 : 2607293 : result = g_utf8_get_char_extended (p, max_len);
779 : :
780 : : /* Disallow codepoint U+0000 as it’s a nul byte,
781 : : * and all string handling in GLib is nul-terminated */
782 : 2607293 : if (result == 0 && max_len > 0)
783 : 2 : return (gunichar) -2;
784 : :
785 : 2607291 : if (result & 0x80000000)
786 : 476 : return result;
787 : 2606815 : else if (!UNICODE_VALID (result))
788 : 0 : return (gunichar)-1;
789 : : else
790 : 2606815 : return result;
791 : : }
792 : :
793 : : #define CONT_BYTE_FAST(p) ((guchar)*p++ & 0x3f)
794 : :
795 : : /**
796 : : * g_utf8_to_ucs4_fast:
797 : : * @str: a UTF-8 encoded string
798 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
799 : : * then the string is nul-terminated.
800 : : * @items_written: (out) (optional): location to store the
801 : : * number of characters in the result, or `NULL`.
802 : : *
803 : : * Convert a string from UTF-8 to a 32-bit fixed width
804 : : * representation as UCS-4, assuming valid UTF-8 input.
805 : : *
806 : : * This function is roughly twice as fast as [func@GLib.utf8_to_ucs4]
807 : : * but does no error checking on the input. A trailing nul character (U+0000)
808 : : * will be added to the string after the converted text.
809 : : *
810 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
811 : : * This value must be freed with [func@GLib.free].
812 : : */
813 : : gunichar *
814 : 23 : g_utf8_to_ucs4_fast (const gchar *str,
815 : : glong len,
816 : : glong *items_written)
817 : : {
818 : : gunichar *result;
819 : : gint n_chars, i;
820 : : const gchar *p;
821 : :
822 : 23 : g_return_val_if_fail (str != NULL, NULL);
823 : :
824 : 23 : p = str;
825 : 23 : n_chars = 0;
826 : 23 : if (len < 0)
827 : : {
828 : 281 : while (*p)
829 : : {
830 : 264 : p = g_utf8_next_char (p);
831 : 264 : ++n_chars;
832 : : }
833 : : }
834 : : else
835 : : {
836 : 255 : while (p < str + len && *p)
837 : : {
838 : 249 : p = g_utf8_next_char (p);
839 : 249 : ++n_chars;
840 : : }
841 : : }
842 : :
843 : 23 : result = g_new (gunichar, n_chars + 1);
844 : :
845 : 23 : p = str;
846 : 536 : for (i=0; i < n_chars; i++)
847 : : {
848 : 513 : guchar first = (guchar)*p++;
849 : : gunichar wc;
850 : :
851 : 513 : if (first < 0xc0)
852 : : {
853 : : /* We really hope first < 0x80, but we don't want to test an
854 : : * extra branch for invalid input, which this function
855 : : * does not care about. Handling unexpected continuation bytes
856 : : * here will do the least damage. */
857 : 231 : wc = first;
858 : : }
859 : : else
860 : : {
861 : 282 : gunichar c1 = CONT_BYTE_FAST(p);
862 : 282 : if (first < 0xe0)
863 : : {
864 : 167 : wc = ((first & 0x1f) << 6) | c1;
865 : : }
866 : : else
867 : : {
868 : 115 : gunichar c2 = CONT_BYTE_FAST(p);
869 : 115 : if (first < 0xf0)
870 : : {
871 : 110 : wc = ((first & 0x0f) << 12) | (c1 << 6) | c2;
872 : : }
873 : : else
874 : : {
875 : 5 : gunichar c3 = CONT_BYTE_FAST(p);
876 : 5 : wc = ((first & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
877 : 5 : if (G_UNLIKELY (first >= 0xf8))
878 : : {
879 : : /* This can't be valid UTF-8, but g_utf8_next_char()
880 : : * and company allow out-of-range sequences */
881 : 0 : gunichar mask = 1 << 20;
882 : 0 : while ((wc & mask) != 0)
883 : : {
884 : 0 : wc <<= 6;
885 : 0 : wc |= CONT_BYTE_FAST(p);
886 : 0 : mask <<= 5;
887 : : }
888 : 0 : wc &= mask - 1;
889 : : }
890 : : }
891 : : }
892 : : }
893 : 513 : result[i] = wc;
894 : : }
895 : 23 : result[i] = 0;
896 : :
897 : 23 : if (items_written)
898 : 15 : *items_written = i;
899 : :
900 : 23 : return result;
901 : : }
902 : :
903 : : static gpointer
904 : 406564 : try_malloc_n (gsize n_blocks, gsize n_block_bytes, GError **error)
905 : : {
906 : 406564 : gpointer ptr = g_try_malloc_n (n_blocks, n_block_bytes);
907 : 406564 : if (ptr == NULL)
908 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_MEMORY,
909 : : _("Failed to allocate memory"));
910 : 406564 : return ptr;
911 : : }
912 : :
913 : : /**
914 : : * g_utf8_to_ucs4:
915 : : * @str: a UTF-8 encoded string
916 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
917 : : * then the string is nul-terminated.
918 : : * @items_read: (out) (optional): location to store number of
919 : : * bytes read, or `NULL`.
920 : : * If `NULL`, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
921 : : * returned in case @str contains a trailing partial
922 : : * character. If an error occurs then the index of the
923 : : * invalid input is stored here.
924 : : * @items_written: (out) (optional): location to store number
925 : : * of characters written or `NULL`. The value here stored does not include
926 : : * the trailing nul character.
927 : : * @error: location to store the error occurring, or `NULL` to ignore
928 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
929 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
930 : : *
931 : : * Convert a string from UTF-8 to a 32-bit fixed width representation as UCS-4.
932 : : *
933 : : * A trailing nul character (U+0000) will be added to the string after the
934 : : * converted text.
935 : : *
936 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
937 : : * This value must be freed with [func@GLib.free].
938 : : */
939 : : gunichar *
940 : 112 : g_utf8_to_ucs4 (const gchar *str,
941 : : glong len,
942 : : glong *items_read,
943 : : glong *items_written,
944 : : GError **error)
945 : : {
946 : 112 : gunichar *result = NULL;
947 : : gint n_chars, i;
948 : : const gchar *in;
949 : :
950 : 112 : in = str;
951 : 112 : n_chars = 0;
952 : 866 : while ((len < 0 || str + len - in > 0) && *in)
953 : : {
954 : 780 : gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
955 : 780 : if (wc & 0x80000000)
956 : : {
957 : 26 : if (wc == (gunichar)-2)
958 : : {
959 : 23 : if (items_read)
960 : 12 : break;
961 : : else
962 : 11 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
963 : : _("Partial character sequence at end of input"));
964 : : }
965 : : else
966 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
967 : : _("Invalid byte sequence in conversion input"));
968 : :
969 : 14 : goto err_out;
970 : : }
971 : :
972 : 754 : n_chars++;
973 : :
974 : 754 : in = g_utf8_next_char (in);
975 : : }
976 : :
977 : 98 : result = try_malloc_n (n_chars + 1, sizeof (gunichar), error);
978 : 98 : if (result == NULL)
979 : 0 : goto err_out;
980 : :
981 : 98 : in = str;
982 : 840 : for (i=0; i < n_chars; i++)
983 : : {
984 : 742 : result[i] = g_utf8_get_char (in);
985 : 742 : in = g_utf8_next_char (in);
986 : : }
987 : 98 : result[i] = 0;
988 : :
989 : 98 : if (items_written)
990 : 78 : *items_written = n_chars;
991 : :
992 : 20 : err_out:
993 : 112 : if (items_read)
994 : 52 : *items_read = in - str;
995 : :
996 : 112 : return result;
997 : : }
998 : :
999 : : /**
1000 : : * g_ucs4_to_utf8:
1001 : : * @str: (array length=len) (element-type gunichar): a UCS-4 encoded string
1002 : : * @len: the maximum length (number of characters) of @str to use.
1003 : : * If @len is negative, then the string is nul-terminated.
1004 : : * @items_read: (out) (optional): location to store number of
1005 : : * characters read, or `NULL`.
1006 : : * @items_written: (out) (optional): location to store number
1007 : : * of bytes written or `NULL`. The value here stored does not include the
1008 : : * trailing nul byte.
1009 : : * @error: location to store the error occurring, or %NULL to ignore
1010 : : * errors. Any of the errors in #GConvertError other than
1011 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1012 : : *
1013 : : * Convert a string from a 32-bit fixed width representation as UCS-4.
1014 : : * to UTF-8.
1015 : : *
1016 : : * The result will be terminated with a nul byte.
1017 : : *
1018 : : * Returns: (transfer full): a pointer to a newly allocated UTF-8 string.
1019 : : * This value must be freed with [func@GLib.free]. If an error occurs,
1020 : : * @items_read will be set to the position of the first invalid input
1021 : : * character.
1022 : : */
1023 : : gchar *
1024 : 406189 : g_ucs4_to_utf8 (const gunichar *str,
1025 : : glong len,
1026 : : glong *items_read,
1027 : : glong *items_written,
1028 : : GError **error)
1029 : : {
1030 : : gint result_length;
1031 : 406189 : gchar *result = NULL;
1032 : : gchar *p;
1033 : : gint i;
1034 : :
1035 : 406189 : result_length = 0;
1036 : 1319705 : for (i = 0; len < 0 || i < len ; i++)
1037 : : {
1038 : 1319701 : if (!str[i])
1039 : 406182 : break;
1040 : :
1041 : 913519 : if (str[i] >= 0x80000000)
1042 : : {
1043 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1044 : : _("Character out of range for UTF-8"));
1045 : 3 : goto err_out;
1046 : : }
1047 : :
1048 : 913516 : result_length += UTF8_LENGTH (str[i]);
1049 : : }
1050 : :
1051 : 406186 : result = try_malloc_n (result_length + 1, 1, error);
1052 : 406186 : if (result == NULL)
1053 : 0 : goto err_out;
1054 : :
1055 : 406186 : p = result;
1056 : :
1057 : 406186 : i = 0;
1058 : 1319696 : while (p < result + result_length)
1059 : 913510 : p += g_unichar_to_utf8 (str[i++], p);
1060 : :
1061 : 406186 : *p = '\0';
1062 : :
1063 : 406186 : if (items_written)
1064 : 17 : *items_written = p - result;
1065 : :
1066 : 406169 : err_out:
1067 : 406189 : if (items_read)
1068 : 19 : *items_read = i;
1069 : :
1070 : 406189 : return result;
1071 : : }
1072 : :
1073 : : #define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000)
1074 : :
1075 : : /**
1076 : : * g_utf16_to_utf8:
1077 : : * @str: (array length=len) (element-type guint16): a UTF-16 encoded string
1078 : : * @len: the maximum length (number of #gunichar2) of @str to use.
1079 : : * If @len is negative, then the string is nul-terminated.
1080 : : * @items_read: (out) (optional): location to store number of words read, or
1081 : : * `NULL`. If `NULL`, then [error@GLib.ConvertError.PARTIAL_INPUT] will
1082 : : * be returned in case @str contains a trailing partial character. If
1083 : : * an error occurs then the index of the invalid input is stored here.
1084 : : * It’s guaranteed to be non-negative.
1085 : : * @items_written: (out) (optional): location to store number
1086 : : * of bytes written, or `NULL`. The value stored here does not include the
1087 : : * trailing nul byte. It’s guaranteed to be non-negative.
1088 : : * @error: location to store the error occurring, or `NULL` to ignore
1089 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1090 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1091 : : *
1092 : : * Convert a string from UTF-16 to UTF-8.
1093 : : *
1094 : : * The result will be terminated with a nul byte.
1095 : : *
1096 : : * Note that the input is expected to be already in native endianness,
1097 : : * an initial byte-order-mark character is not handled specially.
1098 : : * [func@GLib.convert] can be used to convert a byte buffer of UTF-16 data of
1099 : : * ambiguous endianness.
1100 : : *
1101 : : * Further note that this function does not validate the result
1102 : : * string; it may (for example) include embedded nul characters. The only
1103 : : * validation done by this function is to ensure that the input can
1104 : : * be correctly interpreted as UTF-16, i.e. it doesn’t contain
1105 : : * unpaired surrogates or partial character sequences.
1106 : : *
1107 : : * Returns: (transfer full): a pointer to a newly allocated UTF-8 string.
1108 : : * This value must be freed with [func@GLib.free].
1109 : : **/
1110 : : gchar *
1111 : 140 : g_utf16_to_utf8 (const gunichar2 *str,
1112 : : glong len,
1113 : : glong *items_read,
1114 : : glong *items_written,
1115 : : GError **error)
1116 : : {
1117 : : /* This function and g_utf16_to_ucs4 are almost exactly identical -
1118 : : * The lines that differ are marked.
1119 : : */
1120 : : const gunichar2 *in;
1121 : : gchar *out;
1122 : 140 : gchar *result = NULL;
1123 : : gint n_bytes;
1124 : : gunichar high_surrogate;
1125 : :
1126 : 140 : g_return_val_if_fail (str != NULL, NULL);
1127 : :
1128 : 140 : n_bytes = 0;
1129 : 140 : in = str;
1130 : 140 : high_surrogate = 0;
1131 : 2010 : while ((len < 0 || in - str < len) && *in)
1132 : : {
1133 : 1873 : gunichar2 c = *in;
1134 : : gunichar wc;
1135 : :
1136 : 1873 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1137 : : {
1138 : 8 : if (high_surrogate)
1139 : : {
1140 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1141 : 5 : high_surrogate = 0;
1142 : : }
1143 : : else
1144 : : {
1145 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1146 : : _("Invalid sequence in conversion input"));
1147 : 3 : goto err_out;
1148 : : }
1149 : : }
1150 : : else
1151 : : {
1152 : 1865 : if (high_surrogate)
1153 : : {
1154 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1155 : : _("Invalid sequence in conversion input"));
1156 : 0 : goto err_out;
1157 : : }
1158 : :
1159 : 1865 : if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1160 : : {
1161 : 11 : high_surrogate = c;
1162 : 11 : goto next1;
1163 : : }
1164 : : else
1165 : 1854 : wc = c;
1166 : : }
1167 : :
1168 : : /********** DIFFERENT for UTF8/UCS4 **********/
1169 : 1859 : n_bytes += UTF8_LENGTH (wc);
1170 : :
1171 : 1870 : next1:
1172 : 1870 : in++;
1173 : : }
1174 : :
1175 : 137 : if (high_surrogate && !items_read)
1176 : : {
1177 : 4 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1178 : : _("Partial character sequence at end of input"));
1179 : 4 : goto err_out;
1180 : : }
1181 : :
1182 : : /* At this point, everything is valid, and we just need to convert
1183 : : */
1184 : : /********** DIFFERENT for UTF8/UCS4 **********/
1185 : 133 : result = try_malloc_n (n_bytes + 1, 1, error);
1186 : 133 : if (result == NULL)
1187 : 0 : goto err_out;
1188 : :
1189 : 133 : high_surrogate = 0;
1190 : 133 : out = result;
1191 : 133 : in = str;
1192 : 1986 : while (out < result + n_bytes)
1193 : : {
1194 : 1853 : gunichar2 c = *in;
1195 : : gunichar wc;
1196 : :
1197 : 1853 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1198 : : {
1199 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1200 : 5 : high_surrogate = 0;
1201 : : }
1202 : 1848 : else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1203 : : {
1204 : 5 : high_surrogate = c;
1205 : 5 : goto next2;
1206 : : }
1207 : : else
1208 : 1843 : wc = c;
1209 : :
1210 : : /********** DIFFERENT for UTF8/UCS4 **********/
1211 : 1848 : out += g_unichar_to_utf8 (wc, out);
1212 : :
1213 : 1853 : next2:
1214 : 1853 : in++;
1215 : : }
1216 : :
1217 : : /********** DIFFERENT for UTF8/UCS4 **********/
1218 : 133 : *out = '\0';
1219 : :
1220 : 133 : if (items_written)
1221 : : /********** DIFFERENT for UTF8/UCS4 **********/
1222 : 19 : *items_written = out - result;
1223 : :
1224 : 114 : err_out:
1225 : 140 : if (items_read)
1226 : 21 : *items_read = in - str;
1227 : :
1228 : 140 : return result;
1229 : : }
1230 : :
1231 : : /**
1232 : : * g_utf16_to_ucs4:
1233 : : * @str: (array length=len) (element-type guint16): a UTF-16 encoded string
1234 : : * @len: the maximum length (number of #gunichar2) of @str to use.
1235 : : * If @len is negative, then the string is nul-terminated.
1236 : : * @items_read: (out) (optional): location to store number of words read, or
1237 : : * `NULL`. If `NULL`, then [error@GLib.ConvertError.PARTIAL_INPUT] will be
1238 : : * returned in case @str contains a trailing partial character. If
1239 : : * an error occurs then the index of the invalid input is stored here.
1240 : : * @items_written: (out) (optional): location to store number
1241 : : * of characters written, or `NULL`. The value stored here does not include
1242 : : * the trailing nul character.
1243 : : * @error: location to store the error occurring, or `NULL` to ignore
1244 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1245 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1246 : : *
1247 : : * Convert a string from UTF-16 to UCS-4.
1248 : : *
1249 : : * The result will be nul-terminated.
1250 : : *
1251 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
1252 : : * This value must be freed with [func@GLib.free].
1253 : : */
1254 : : gunichar *
1255 : 25 : g_utf16_to_ucs4 (const gunichar2 *str,
1256 : : glong len,
1257 : : glong *items_read,
1258 : : glong *items_written,
1259 : : GError **error)
1260 : : {
1261 : : const gunichar2 *in;
1262 : : gchar *out;
1263 : 25 : gchar *result = NULL;
1264 : : size_t n_bytes;
1265 : : gunichar high_surrogate;
1266 : :
1267 : 25 : g_return_val_if_fail (str != NULL, NULL);
1268 : :
1269 : 25 : n_bytes = 0;
1270 : 25 : in = str;
1271 : 25 : high_surrogate = 0;
1272 : 84 : while ((len < 0 || in - str < len) && *in)
1273 : : {
1274 : 62 : gunichar2 c = *in;
1275 : :
1276 : 62 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1277 : : {
1278 : 8 : if (high_surrogate)
1279 : : {
1280 : 5 : high_surrogate = 0;
1281 : : }
1282 : : else
1283 : : {
1284 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1285 : : _("Invalid sequence in conversion input"));
1286 : 3 : goto err_out;
1287 : : }
1288 : : }
1289 : : else
1290 : : {
1291 : 54 : if (high_surrogate)
1292 : : {
1293 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1294 : : _("Invalid sequence in conversion input"));
1295 : 0 : goto err_out;
1296 : : }
1297 : :
1298 : 54 : if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1299 : : {
1300 : 8 : high_surrogate = c;
1301 : 8 : goto next1;
1302 : : }
1303 : : }
1304 : :
1305 : : /********** DIFFERENT for UTF8/UCS4 **********/
1306 : 51 : n_bytes += sizeof (gunichar);
1307 : :
1308 : 59 : next1:
1309 : 59 : in++;
1310 : : }
1311 : :
1312 : 22 : if (high_surrogate && !items_read)
1313 : : {
1314 : 1 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1315 : : _("Partial character sequence at end of input"));
1316 : 1 : goto err_out;
1317 : : }
1318 : :
1319 : : /* At this point, everything is valid, and we just need to convert
1320 : : */
1321 : : /********** DIFFERENT for UTF8/UCS4 **********/
1322 : 21 : result = try_malloc_n (n_bytes + 4, 1, error);
1323 : 21 : if (result == NULL)
1324 : 0 : goto err_out;
1325 : :
1326 : 21 : high_surrogate = 0;
1327 : 21 : out = result;
1328 : 21 : in = str;
1329 : 69 : while (out < result + n_bytes)
1330 : : {
1331 : 48 : gunichar2 c = *in;
1332 : : gunichar wc;
1333 : :
1334 : 48 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1335 : : {
1336 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1337 : 5 : high_surrogate = 0;
1338 : : }
1339 : 43 : else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1340 : : {
1341 : 5 : high_surrogate = c;
1342 : 5 : goto next2;
1343 : : }
1344 : : else
1345 : 38 : wc = c;
1346 : :
1347 : : /********** DIFFERENT for UTF8/UCS4 **********/
1348 : 43 : *(gunichar *)out = wc;
1349 : 43 : out += sizeof (gunichar);
1350 : :
1351 : 48 : next2:
1352 : 48 : in++;
1353 : : }
1354 : :
1355 : : /********** DIFFERENT for UTF8/UCS4 **********/
1356 : 21 : *(gunichar *)out = 0;
1357 : :
1358 : 21 : if (items_written)
1359 : : /********** DIFFERENT for UTF8/UCS4 **********/
1360 : 19 : *items_written = (out - result) / sizeof (gunichar);
1361 : :
1362 : 2 : err_out:
1363 : 25 : if (items_read)
1364 : 21 : *items_read = in - str;
1365 : :
1366 : 25 : return (gunichar *)result;
1367 : : }
1368 : :
1369 : : /**
1370 : : * g_utf8_to_utf16:
1371 : : * @str: a UTF-8 encoded string
1372 : : * @len: the maximum length (number of bytes) of @str to use.
1373 : : * If @len is negative, then the string is nul-terminated.
1374 : : * @items_read: (out) (optional): location to store number of bytes read, or
1375 : : * `NULL`. If `NULL`, then [error@GLib.ConvertError.PARTIAL_INPUT] will
1376 : : * be returned in case @str contains a trailing partial character. If
1377 : : * an error occurs then the index of the invalid input is stored here.
1378 : : * @items_written: (out) (optional): location to store number
1379 : : * of `gunichar2` written, or `NULL`. The value stored here does not include
1380 : : * the trailing nul.
1381 : : * @error: location to store the error occurring, or `NULL` to ignore
1382 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1383 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1384 : : *
1385 : : * Convert a string from UTF-8 to UTF-16.
1386 : : *
1387 : : * A nul character (U+0000) will be added to the result after the converted text.
1388 : : *
1389 : : * Returns: (transfer full): a pointer to a newly allocated UTF-16 string.
1390 : : * This value must be freed with [func@GLib.free].
1391 : : */
1392 : : gunichar2 *
1393 : 111 : g_utf8_to_utf16 (const gchar *str,
1394 : : glong len,
1395 : : glong *items_read,
1396 : : glong *items_written,
1397 : : GError **error)
1398 : : {
1399 : 111 : gunichar2 *result = NULL;
1400 : : gint n16;
1401 : : const gchar *in;
1402 : : gint i;
1403 : :
1404 : 111 : g_return_val_if_fail (str != NULL, NULL);
1405 : :
1406 : 111 : in = str;
1407 : 111 : n16 = 0;
1408 : 4445 : while ((len < 0 || str + len - in > 0) && *in)
1409 : : {
1410 : 4340 : gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
1411 : 4340 : if (wc & 0x80000000)
1412 : : {
1413 : 6 : if (wc == (gunichar)-2)
1414 : : {
1415 : 3 : if (items_read)
1416 : 2 : break;
1417 : : else
1418 : 1 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1419 : : _("Partial character sequence at end of input"));
1420 : : }
1421 : : else
1422 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1423 : : _("Invalid byte sequence in conversion input"));
1424 : :
1425 : 4 : goto err_out;
1426 : : }
1427 : :
1428 : 4334 : if (wc < 0xd800)
1429 : 4325 : n16 += 1;
1430 : 9 : else if (wc < 0xe000)
1431 : : {
1432 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1433 : : _("Invalid sequence in conversion input"));
1434 : :
1435 : 0 : goto err_out;
1436 : : }
1437 : 9 : else if (wc < 0x10000)
1438 : 3 : n16 += 1;
1439 : 6 : else if (wc < 0x110000)
1440 : 6 : n16 += 2;
1441 : : else
1442 : : {
1443 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1444 : : _("Character out of range for UTF-16"));
1445 : :
1446 : 0 : goto err_out;
1447 : : }
1448 : :
1449 : 4334 : in = g_utf8_next_char (in);
1450 : : }
1451 : :
1452 : 107 : result = try_malloc_n (n16 + 1, sizeof (gunichar2), error);
1453 : 107 : if (result == NULL)
1454 : 0 : goto err_out;
1455 : :
1456 : 107 : in = str;
1457 : 4429 : for (i = 0; i < n16;)
1458 : : {
1459 : 4322 : gunichar wc = g_utf8_get_char (in);
1460 : :
1461 : 4322 : if (wc < 0x10000)
1462 : : {
1463 : 4316 : result[i++] = wc;
1464 : : }
1465 : : else
1466 : : {
1467 : 6 : result[i++] = (wc - 0x10000) / 0x400 + 0xd800;
1468 : 6 : result[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
1469 : : }
1470 : :
1471 : 4322 : in = g_utf8_next_char (in);
1472 : : }
1473 : :
1474 : 107 : result[i] = 0;
1475 : :
1476 : 107 : if (items_written)
1477 : 23 : *items_written = n16;
1478 : :
1479 : 84 : err_out:
1480 : 111 : if (items_read)
1481 : 21 : *items_read = in - str;
1482 : :
1483 : 111 : return result;
1484 : : }
1485 : :
1486 : : /**
1487 : : * g_ucs4_to_utf16:
1488 : : * @str: (array length=len) (element-type gunichar): a UCS-4 encoded string
1489 : : * @len: the maximum length (number of characters) of @str to use.
1490 : : * If @len is negative, then the string is nul-terminated.
1491 : : * @items_read: (out) (optional): location to store number of
1492 : : * bytes read, or `NULL`. If an error occurs then the index of the invalid
1493 : : * input is stored here.
1494 : : * @items_written: (out) (optional): location to store number
1495 : : * of `gunichar2` written, or `NULL`. The value stored here does not include
1496 : : * the trailing nul.
1497 : : * @error: location to store the error occurring, or `NULL` to ignore
1498 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1499 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1500 : : *
1501 : : * Convert a string from UCS-4 to UTF-16.
1502 : : *
1503 : : * A nul character (U+0000) will be added to the result after the converted text.
1504 : : *
1505 : : * Returns: (transfer full): a pointer to a newly allocated UTF-16 string.
1506 : : * This value must be freed with [func@GLib.free].
1507 : : */
1508 : : gunichar2 *
1509 : 22 : g_ucs4_to_utf16 (const gunichar *str,
1510 : : glong len,
1511 : : glong *items_read,
1512 : : glong *items_written,
1513 : : GError **error)
1514 : : {
1515 : 22 : gunichar2 *result = NULL;
1516 : : gint n16;
1517 : : gint i, j;
1518 : :
1519 : 22 : n16 = 0;
1520 : 22 : i = 0;
1521 : 67 : while ((len < 0 || i < len) && str[i])
1522 : : {
1523 : 48 : gunichar wc = str[i];
1524 : :
1525 : 48 : if (wc < 0xd800)
1526 : 37 : n16 += 1;
1527 : 11 : else if (wc < 0xe000)
1528 : : {
1529 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1530 : : _("Invalid sequence in conversion input"));
1531 : :
1532 : 0 : goto err_out;
1533 : : }
1534 : 11 : else if (wc < 0x10000)
1535 : 3 : n16 += 1;
1536 : 8 : else if (wc < 0x110000)
1537 : 5 : n16 += 2;
1538 : : else
1539 : : {
1540 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1541 : : _("Character out of range for UTF-16"));
1542 : :
1543 : 3 : goto err_out;
1544 : : }
1545 : :
1546 : 45 : i++;
1547 : : }
1548 : :
1549 : 19 : result = try_malloc_n (n16 + 1, sizeof (gunichar2), error);
1550 : 19 : if (result == NULL)
1551 : 0 : goto err_out;
1552 : :
1553 : 58 : for (i = 0, j = 0; j < n16; i++)
1554 : : {
1555 : 39 : gunichar wc = str[i];
1556 : :
1557 : 39 : if (wc < 0x10000)
1558 : : {
1559 : 34 : result[j++] = wc;
1560 : : }
1561 : : else
1562 : : {
1563 : 5 : result[j++] = (wc - 0x10000) / 0x400 + 0xd800;
1564 : 5 : result[j++] = (wc - 0x10000) % 0x400 + 0xdc00;
1565 : : }
1566 : : }
1567 : 19 : result[j] = 0;
1568 : :
1569 : 19 : if (items_written)
1570 : 17 : *items_written = n16;
1571 : :
1572 : 2 : err_out:
1573 : 22 : if (items_read)
1574 : 19 : *items_read = i;
1575 : :
1576 : 22 : return result;
1577 : : }
1578 : :
1579 : : /**< private >
1580 : : * find_invalid_or_incomplete_utf8_sequence:
1581 : : *
1582 : : * @string: the source string.
1583 : : *
1584 : : * Returns the first byte of a sequence that is either invalid
1585 : : * UTF-8 or incomplete UTF-8, or a pointer to the NULL terminator
1586 : : * if all of @string is valid UTF-8.
1587 : : */
1588 : : static const char *
1589 : 24 : find_invalid_or_incomplete_utf8_sequence (const char *string)
1590 : : {
1591 : 24 : const char *end = string;
1592 : :
1593 : 24 : g_utf8_validate (string, -1, &end);
1594 : :
1595 : 24 : return end;
1596 : : }
1597 : :
1598 : : /**< private >
1599 : : * find_valid_and_complete_utf8_sequence:
1600 : : *
1601 : : * @string: a NULL-terminated source string.
1602 : : *
1603 : : * Returns the first byte of a sequence that is valid (and complete)
1604 : : * UTF-8, or a pointer to the NULL terminator if no such sequence
1605 : : * could be found.
1606 : : */
1607 : : static const char *
1608 : 14 : find_valid_and_complete_utf8_sequence (const char *string)
1609 : : {
1610 : 14 : const unsigned char *iter = (const unsigned char *)string;
1611 : :
1612 : 36 : for (;; iter++)
1613 : : {
1614 : 50 : if (*iter < 128 ||
1615 : 40 : ((*iter & 0xC0) == 0xC0 &&
1616 : 40 : g_utf8_get_char_validated ((const char*)iter, -1) < (gunichar2)-2))
1617 : : {
1618 : : break;
1619 : : }
1620 : : }
1621 : :
1622 : 14 : return (const char *) iter;
1623 : : }
1624 : :
1625 : :
1626 : : /**< private >
1627 : : * invalidly_encoded_string_to_utf16_get_output_length:
1628 : : *
1629 : : * @start: start of the source string.
1630 : : * @end: end of the source string (excluded).
1631 : : *
1632 : : * Returns the output length, as a count of gunichar2, that is necessary
1633 : : * for the generic translation of an invalidly-encoded string to UTF-16.
1634 : : */
1635 : : static size_t
1636 : 9 : invalidly_encoded_string_to_utf16_get_output_length (const char *start,
1637 : : const char *end)
1638 : : {
1639 : : size_t count;
1640 : :
1641 : 9 : g_assert ((uintptr_t)end >= (uintptr_t)start);
1642 : :
1643 : : /* We output one gunichar2 for each input byte */
1644 : 9 : count = (uintptr_t)end - (uintptr_t)start;
1645 : :
1646 : 9 : return count;
1647 : : }
1648 : :
1649 : : /**< private >
1650 : : * invalidly_encoded_string_to_utf16:
1651 : : *
1652 : : * @start: start of the string.
1653 : : * @end: end of the string (excluded).
1654 : : * @output: the output buffer. Must be long enough to hold
1655 : : * the entire output.
1656 : : *
1657 : : * Performs a generic conversion of an invalidly-encoded string
1658 : : * to UTF-16. Note: the current implementation simply outputs
1659 : : * Unicode Replacement Characters "�" (U+FFFD) for each byte in
1660 : : * the source string.
1661 : : */
1662 : : static size_t
1663 : 5 : invalidly_encoded_string_to_utf16 (const char *start,
1664 : : const char *end,
1665 : : gunichar2 *output)
1666 : : {
1667 : : size_t count;
1668 : :
1669 : 5 : g_assert ((uintptr_t)end >= (uintptr_t)start);
1670 : 5 : count = (uintptr_t)end - (uintptr_t)start;
1671 : :
1672 : 19 : for (size_t i = 0; i < count; i++)
1673 : 14 : output[i] = 0xFFFD;
1674 : :
1675 : 5 : return count;
1676 : : }
1677 : :
1678 : : /**< private >
1679 : : * invalidly_encoded_string_to_utf16_backtrack:
1680 : : *
1681 : : * @start: start of the source string.
1682 : : * @output_length: length within the output UTF-16 string
1683 : : * expressed as a count of gunichar2.
1684 : : *
1685 : : * Backtracks an output-length in count of gunichar2 to the
1686 : : * corresponding length, in bytes, of the source string.
1687 : : */
1688 : : static size_t
1689 : 2 : invalidly_encoded_string_to_utf16_backtrack (const char *start,
1690 : : size_t output_length)
1691 : : {
1692 : : /* The conversion process outputs one gunichar2 (a complete
1693 : : * character) for each input byte, so the mapping is very
1694 : : * simple.
1695 : : */
1696 : 2 : return output_length;
1697 : : }
1698 : :
1699 : :
1700 : : /**< private >
1701 : : * valid_utf8_to_utf16_get_output_length:
1702 : : *
1703 : : * @start: start of the source string. Must be valid UTF-8.
1704 : : * @end: end of the source string (excluded).
1705 : : *
1706 : : * Returns the output-length, in count of gunichar2, necessary for the
1707 : : * translation of a valid UTF-8 string to UTF-16.
1708 : : */
1709 : : static size_t
1710 : 17 : valid_utf8_to_utf16_get_output_length (const char *start,
1711 : : const char *end)
1712 : : {
1713 : 17 : size_t count = 0;
1714 : :
1715 : 87 : while (start < end)
1716 : : {
1717 : 70 : gunichar codepoint = g_utf8_get_char (start);
1718 : :
1719 : 70 : if (codepoint <= 0xFFFF)
1720 : 58 : count += 1;
1721 : : else
1722 : 12 : count += 2;
1723 : :
1724 : 70 : start = g_utf8_next_char (start);
1725 : : }
1726 : :
1727 : 17 : g_assert (start == end);
1728 : :
1729 : 17 : return count;
1730 : : }
1731 : :
1732 : : /**< private >
1733 : : * valid_utf8_to_utf16:
1734 : : *
1735 : : * @start: start of the source string. Must be valid UTF-8
1736 : : * @end: end of the source string (excluded).
1737 : : * @output: the output buffer. Must be long enough to hold
1738 : : * the entire output.
1739 : : *
1740 : : * Performs the conversion of a valid UTF-8 string to UTF-16.
1741 : : */
1742 : : static size_t
1743 : 7 : valid_utf8_to_utf16 (const char *start,
1744 : : const char *end,
1745 : : gunichar2 *output)
1746 : : {
1747 : 7 : size_t count = 0;
1748 : :
1749 : 41 : while (start < end)
1750 : : {
1751 : 34 : gunichar codepoint = g_utf8_get_char (start);
1752 : :
1753 : 34 : if (codepoint <= 0xFFFF)
1754 : : {
1755 : 31 : output[count++] = (gunichar2) codepoint;
1756 : : }
1757 : : else
1758 : : {
1759 : 3 : gunichar subtract = codepoint - 0x010000;
1760 : 3 : output[count++] = 0xD800 + ((subtract >> 10) & 0x3FF);
1761 : 3 : output[count++] = 0xDC00 + (subtract & 0x3FF);
1762 : : }
1763 : :
1764 : 34 : start = g_utf8_next_char (start);
1765 : : }
1766 : :
1767 : 7 : g_assert (start == end);
1768 : :
1769 : 7 : return count;
1770 : : }
1771 : :
1772 : : /**< private >
1773 : : * valid_utf8_to_utf16_backtrack:
1774 : : *
1775 : : * @start: start of the source string. Must be valid UTF-8.
1776 : : * @output_length: length within the output UTF-16 string expressed
1777 : : * as a count of gunichar2.
1778 : : *
1779 : : * Backtracks an output-length in count of gunichar2 to the
1780 : : * corresponding length, in bytes, of the source string.
1781 : : */
1782 : : static size_t
1783 : 5 : valid_utf8_to_utf16_backtrack (const char *start,
1784 : : size_t output_length)
1785 : : {
1786 : 5 : const char *iter = start;
1787 : 5 : size_t count = 0;
1788 : :
1789 : 15 : for (; *iter != '\0'; iter = g_utf8_next_char (iter))
1790 : : {
1791 : 15 : if (output_length <= count)
1792 : 5 : break;
1793 : :
1794 : 10 : if (g_utf8_get_char (iter) <= 0xFFFF)
1795 : 9 : count += 1;
1796 : : else
1797 : 1 : count += 2;
1798 : : }
1799 : :
1800 : 5 : return (uintptr_t)iter - (uintptr_t)start;
1801 : : }
1802 : :
1803 : :
1804 : : static size_t
1805 : 3 : utf8_to_utf16_make_valid_get_output_length (const char *string)
1806 : : {
1807 : 3 : const char *start = string;
1808 : 3 : size_t count = 0;
1809 : :
1810 : : while (true)
1811 : 4 : {
1812 : 7 : const char *end = NULL;
1813 : :
1814 : 7 : end = find_invalid_or_incomplete_utf8_sequence (start);
1815 : 7 : count += valid_utf8_to_utf16_get_output_length (start, end);
1816 : 7 : start = end;
1817 : :
1818 : 7 : if (start[0] == '\0')
1819 : 2 : break;
1820 : :
1821 : 5 : end = find_valid_and_complete_utf8_sequence (start);
1822 : 5 : g_assert ((uintptr_t)end > (uintptr_t)start);
1823 : 5 : count += invalidly_encoded_string_to_utf16_get_output_length (start, end);
1824 : 5 : start = end;
1825 : :
1826 : 5 : if (start[0] == '\0')
1827 : 1 : break;
1828 : : }
1829 : :
1830 : 3 : return count;
1831 : : }
1832 : :
1833 : : static size_t
1834 : 10 : utf8_to_utf16_make_valid_backtrack (const char *string,
1835 : : size_t output_length)
1836 : : {
1837 : 10 : const char *start = string;
1838 : 10 : size_t count = 0;
1839 : : size_t l;
1840 : :
1841 : : while (true)
1842 : 0 : {
1843 : 10 : const char *end = NULL;
1844 : :
1845 : 10 : end = find_invalid_or_incomplete_utf8_sequence (start);
1846 : 10 : l = valid_utf8_to_utf16_get_output_length (start, end);
1847 : 10 : if (output_length < count + l)
1848 : 5 : return count + valid_utf8_to_utf16_backtrack (start, output_length);
1849 : 5 : count += (uintptr_t)end - (uintptr_t)start;
1850 : 5 : output_length -= l;
1851 : 5 : start = end;
1852 : :
1853 : 5 : if (start[0] == '\0')
1854 : 1 : return (uintptr_t)start - (uintptr_t)string;
1855 : :
1856 : 4 : end = find_valid_and_complete_utf8_sequence (start);
1857 : 4 : g_assert ((uintptr_t)end > (uintptr_t)start);
1858 : 4 : l = invalidly_encoded_string_to_utf16_get_output_length (start, end);
1859 : 4 : if (output_length < l)
1860 : 2 : return count + invalidly_encoded_string_to_utf16_backtrack (start, output_length);
1861 : 2 : count += (uintptr_t)end - (uintptr_t)start;
1862 : 2 : output_length -= l;
1863 : 2 : start = end;
1864 : :
1865 : 2 : if (start[0] == '\0')
1866 : 2 : return (uintptr_t)start - (uintptr_t)string;
1867 : : }
1868 : :
1869 : : return count;
1870 : : }
1871 : :
1872 : :
1873 : : static size_t
1874 : 3 : utf8_to_utf16_make_valid (const char *string,
1875 : : gunichar2 *output)
1876 : : {
1877 : 3 : const char *start = string;
1878 : 3 : size_t count = 0;
1879 : :
1880 : : while (true)
1881 : 4 : {
1882 : 7 : const char *end = NULL;
1883 : :
1884 : 7 : end = find_invalid_or_incomplete_utf8_sequence (start);
1885 : 7 : count += valid_utf8_to_utf16 (start, end, &output[count]);
1886 : 7 : start = end;
1887 : :
1888 : 7 : if (start[0] == '\0')
1889 : 2 : break;
1890 : :
1891 : 5 : end = find_valid_and_complete_utf8_sequence (start);
1892 : 5 : g_assert ((uintptr_t)end > (uintptr_t)start);
1893 : 5 : count += invalidly_encoded_string_to_utf16 (start, end, &output[count]);
1894 : 5 : start = end;
1895 : :
1896 : 5 : if (start[0] == '\0')
1897 : 1 : break;
1898 : : }
1899 : :
1900 : 3 : return count;
1901 : : }
1902 : :
1903 : : /** < private >
1904 : : * g_utf8_to_utf16_make_valid:
1905 : : *
1906 : : * @utf8: source UTF-8 string. May contain invalid or incomplete sequences.
1907 : : * @buffer: optional auxiliary buffer where the output UTF-16 string will be
1908 : : * stored if large enough to hold the output. Callers can pass NULL,
1909 : : * in which case the output buffer is allocated on the heap.
1910 : : * @buffer_len: length, in count of gunichar2, of @buffer. This is used only
1911 : : * if @buffer is not NULL.
1912 : : * @out_utf16: pointer that will be set the to output string. If @buffer is
1913 : : * long enough to hold the data, *out_utf16 will equal @buffer
1914 : : * upon return; otherwise *out_utf16 will point to heap-allocated
1915 : : * data, which must be freed using `g_free`.
1916 : : * @out_utf16_len: pointer to size_t that will be set to the length of the
1917 : : * output UTF-16 string on return, in count of gunichar2.
1918 : : * Can be NULL.
1919 : : *
1920 : : * Performs conversion of an UTF-8 string that may contain invalid sequences
1921 : : * to UTF-16.
1922 : : *
1923 : : * On return, the caller should check if *out_utf16 equals @buffer and call
1924 : : * `g_free` accordingly.
1925 : : */
1926 : : void
1927 : 3 : g_utf8_to_utf16_make_valid (const char *utf8,
1928 : : gunichar2 *buffer,
1929 : : size_t buffer_len,
1930 : : gunichar2 **out_utf16,
1931 : : size_t *out_utf16_len)
1932 : : {
1933 : 3 : size_t output_length = utf8_to_utf16_make_valid_get_output_length (utf8);
1934 : :
1935 : 3 : if (output_length < buffer_len)
1936 : : {
1937 : 2 : *out_utf16 = buffer;
1938 : : }
1939 : : else
1940 : : {
1941 : : /* output_length cannot be greater than strlen (utf8), which
1942 : : * is less than SIZE_MAX since utf8 is null-terminated.
1943 : : * As such, (output_length + 1) cannot overflow.
1944 : : */
1945 : 1 : *out_utf16 = g_new (gunichar2, output_length + 1);
1946 : : }
1947 : :
1948 : 3 : utf8_to_utf16_make_valid (utf8, *out_utf16);
1949 : :
1950 : : /* Add the terminating NULL character */
1951 : 3 : (*out_utf16)[output_length] = L'\0';
1952 : :
1953 : 3 : if (out_utf16_len)
1954 : 2 : *out_utf16_len = output_length;
1955 : 3 : }
1956 : :
1957 : : /** < private >
1958 : : * g_utf8_to_utf16_make_valid_backtrack:
1959 : : *
1960 : : * @utf8: source UTF-8 string. May contain invalid or incomplete sequences.
1961 : : * @utf16_len: length within the output UTF-16 string expressed as a count
1962 : : * of gunichar2.
1963 : : *
1964 : : * Backtracks an output-length in count of gunichar2 to the
1965 : : * corresponding length, in bytes, of the source string.
1966 : : */
1967 : : size_t
1968 : 10 : g_utf8_to_utf16_make_valid_backtrack (const char *utf8,
1969 : : size_t utf16_len)
1970 : : {
1971 : 10 : return utf8_to_utf16_make_valid_backtrack (utf8, utf16_len);
1972 : : }
1973 : :
1974 : : /* SIMD-based UTF-8 validation originates in the c-utf8 project from
1975 : : * https://github.com/c-util/c-utf8/ from the following authors:
1976 : : *
1977 : : * David Rheinsberg <david@readahead.eu>
1978 : : * Evgeny Vereshchagin <evvers@ya.ru>
1979 : : * Jan Engelhardt <jengelh@inai.de>
1980 : : * Tom Gundersen <teg@jklm.no>
1981 : : *
1982 : : * It has been adapted for portability and integration.
1983 : : * The original code is dual-licensed Apache-2.0 or LGPLv2.1+
1984 : : */
1985 : :
1986 : : #define align_to(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1))
1987 : :
1988 : : static inline guint8
1989 : 52065952 : load_u8 (gconstpointer memory,
1990 : : gsize offset)
1991 : : {
1992 : 52065952 : return ((const guint8 *)memory)[offset];
1993 : : }
1994 : :
1995 : : #if G_GNUC_CHECK_VERSION(4,8) || defined(__clang__)
1996 : : # define _attribute_aligned(n) __attribute__((aligned(n)))
1997 : : #elif defined(_MSC_VER)
1998 : : # define _attribute_aligned(n) __declspec(align(n))
1999 : : #else
2000 : : # define _attribute_aligned(n)
2001 : : #endif
2002 : :
2003 : : static inline gsize
2004 : 32779197 : load_word (gconstpointer memory,
2005 : : gsize offset)
2006 : : {
2007 : : #if GLIB_SIZEOF_VOID_P == 8
2008 : 32779197 : _attribute_aligned(8) const guint8 *m = ((const guint8 *)memory) + offset;
2009 : :
2010 : 32779197 : return ((guint64)m[0] << 0) | ((guint64)m[1] << 8) |
2011 : 32779197 : ((guint64)m[2] << 16) | ((guint64)m[3] << 24) |
2012 : 32779197 : ((guint64)m[4] << 32) | ((guint64)m[5] << 40) |
2013 : 32779197 : ((guint64)m[6] << 48) | ((guint64)m[7] << 56);
2014 : : #else
2015 : : _attribute_aligned(4) const guint8 *m = ((const guint8 *)memory) + offset;
2016 : :
2017 : : return ((guint)m[0] << 0) | ((guint)m[1] << 8) |
2018 : : ((guint)m[2] << 16) | ((guint)m[3] << 24);
2019 : : #endif
2020 : : }
2021 : :
2022 : : /* The following constants are truncated on 32-bit machines */
2023 : : #define UTF8_ASCII_MASK ((gsize)0x8080808080808080L)
2024 : : #define UTF8_ASCII_SUB ((gsize)0x0101010101010101L)
2025 : :
2026 : : static inline int
2027 : 32779197 : utf8_word_is_ascii (gsize word)
2028 : : {
2029 : : /* True unless any byte is NULL or has the MSB set. */
2030 : 32779197 : return ((((word - UTF8_ASCII_SUB) | word) & UTF8_ASCII_MASK) == 0);
2031 : : }
2032 : :
2033 : : static void
2034 : 2754036 : utf8_verify_ascii (const char **strp,
2035 : : gsize *lenp)
2036 : : {
2037 : 2754036 : const char *str = *strp;
2038 : 2754036 : gsize len = lenp ? *lenp : strlen (str);
2039 : :
2040 : 11068702 : while (len > 0 && load_u8 (str, 0) < 128)
2041 : : {
2042 : 9716605 : if ((gpointer) align_to ((guintptr) str, sizeof (gsize)) == str)
2043 : : {
2044 : 18685606 : while (len >= 2 * sizeof (gsize))
2045 : : {
2046 : 32779197 : if (!utf8_word_is_ascii (load_word (str, 0)) ||
2047 : 16384497 : !utf8_word_is_ascii (load_word (str, sizeof (gsize))))
2048 : : break;
2049 : :
2050 : 16301338 : str += 2 * sizeof(gsize);
2051 : 16301338 : len -= 2 * sizeof(gsize);
2052 : : }
2053 : :
2054 : 16526015 : while (len > 0 && load_u8 (str, 0) < 128)
2055 : : {
2056 : 15354433 : if G_UNLIKELY (load_u8 (str, 0) == 0x00)
2057 : 1212686 : goto out;
2058 : :
2059 : 14141747 : ++str;
2060 : 14141747 : --len;
2061 : : }
2062 : : }
2063 : : else
2064 : : {
2065 : 7332337 : if G_UNLIKELY (load_u8 (str, 0) == 0x00)
2066 : 189253 : goto out;
2067 : :
2068 : 7143084 : ++str;
2069 : 7143084 : --len;
2070 : : }
2071 : : }
2072 : :
2073 : 1352097 : out:
2074 : 2754036 : *strp = str;
2075 : :
2076 : 2754036 : if (lenp)
2077 : 2751557 : *lenp = len;
2078 : 2754036 : }
2079 : :
2080 : : #define UTF8_CHAR_IS_TAIL(_x) (((_x) & 0xC0) == 0x80)
2081 : :
2082 : : static void
2083 : 2743232 : utf8_verify (const char **strp,
2084 : : gsize *lenp)
2085 : : {
2086 : 2743232 : const char *str = *strp;
2087 : 2743232 : gsize len = lenp ? *lenp : strlen (str);
2088 : :
2089 : : /* See Unicode 10.0.0, Chapter 3, Section D92 */
2090 : :
2091 : 5527617 : while (len > 0)
2092 : : {
2093 : 4188437 : guint8 b = load_u8 (str, 0);
2094 : :
2095 : 4188437 : if (b == 0x00)
2096 : 1403250 : goto out;
2097 : :
2098 : 2785187 : else if (b <= 0x7F)
2099 : : {
2100 : : /*
2101 : : * Special-case and optimize the ASCII case.
2102 : : */
2103 : 2751557 : utf8_verify_ascii ((const char **)&str, &len);
2104 : : }
2105 : :
2106 : 33630 : else if (b >= 0xC2 && b <= 0xDF)
2107 : : {
2108 : 3423 : if G_UNLIKELY (len < 2)
2109 : 10 : goto out;
2110 : 3413 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
2111 : 107 : goto out;
2112 : :
2113 : 3306 : str += 2;
2114 : 3306 : len -= 2;
2115 : :
2116 : : }
2117 : :
2118 : 30207 : else if (b == 0xE0)
2119 : : {
2120 : 1056 : if G_UNLIKELY (len < 3)
2121 : 5 : goto out;
2122 : 1051 : if G_UNLIKELY (load_u8 (str, 1) < 0xA0 || load_u8 (str, 1) > 0xBF)
2123 : 16 : goto out;
2124 : 1035 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2125 : 18 : goto out;
2126 : :
2127 : 1017 : str += 3;
2128 : 1017 : len -= 3;
2129 : : }
2130 : :
2131 : 29151 : else if (b >= 0xE1 && b <= 0xEC)
2132 : : {
2133 : 28246 : if G_UNLIKELY (len < 3)
2134 : 32 : goto out;
2135 : 28214 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
2136 : 36 : goto out;
2137 : 28178 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2138 : 2 : goto out;
2139 : :
2140 : 28176 : str += 3;
2141 : 28176 : len -= 3;
2142 : : }
2143 : :
2144 : 905 : else if (b == 0xED)
2145 : : {
2146 : 124 : if G_UNLIKELY (len < 3)
2147 : 2 : goto out;
2148 : 122 : if G_UNLIKELY (load_u8 (str, 1) < 0x80 || load_u8 (str, 1) > 0x9F)
2149 : 45 : goto out;
2150 : 77 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2151 : 2 : goto out;
2152 : :
2153 : 75 : str += 3;
2154 : 75 : len -= 3;
2155 : : }
2156 : :
2157 : 781 : else if (b >= 0xEE && b <= 0xEF)
2158 : : {
2159 : 224 : if G_UNLIKELY (len < 3)
2160 : 7 : goto out;
2161 : 217 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
2162 : 2 : goto out;
2163 : 215 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2164 : 0 : goto out;
2165 : :
2166 : 215 : str += 3;
2167 : 215 : len -= 3;
2168 : : }
2169 : :
2170 : 557 : else if (b == 0xF0)
2171 : : {
2172 : 58 : if G_UNLIKELY (len < 4)
2173 : 5 : goto out;
2174 : 53 : if G_UNLIKELY (load_u8 (str, 1) < 0x90 || load_u8 (str, 1) > 0xBF)
2175 : 17 : goto out;
2176 : 36 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2177 : 2 : goto out;
2178 : 34 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 3)))
2179 : 2 : goto out;
2180 : :
2181 : 32 : str += 4;
2182 : 32 : len -= 4;
2183 : : }
2184 : :
2185 : 499 : else if (b >= 0xF1 && b <= 0xF3)
2186 : : {
2187 : 17 : if G_UNLIKELY (len < 4)
2188 : 6 : goto out;
2189 : 11 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
2190 : 5 : goto out;
2191 : 6 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2192 : 2 : goto out;
2193 : 4 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 3)))
2194 : 2 : goto out;
2195 : :
2196 : 2 : str += 4;
2197 : 2 : len -= 4;
2198 : : }
2199 : :
2200 : 482 : else if (b == 0xF4)
2201 : : {
2202 : 36 : if G_UNLIKELY (len < 4)
2203 : 13 : goto out;
2204 : 23 : if G_UNLIKELY (load_u8 (str, 1) < 0x80 || load_u8 (str, 1) > 0x8F)
2205 : 14 : goto out;
2206 : 9 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
2207 : 2 : goto out;
2208 : 7 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 3)))
2209 : 2 : goto out;
2210 : :
2211 : 5 : str += 4;
2212 : 5 : len -= 4;
2213 : : }
2214 : :
2215 : 446 : else goto out;
2216 : : }
2217 : :
2218 : 1339180 : out:
2219 : 2743232 : *strp = str;
2220 : :
2221 : 2743232 : if (lenp)
2222 : 2743232 : *lenp = len;
2223 : 2743232 : }
2224 : :
2225 : : /**
2226 : : * g_utf8_validate:
2227 : : * @str: (array length=max_len) (element-type guint8): a pointer to character data
2228 : : * @max_len: max bytes to validate, or `-1` to go until nul
2229 : : * @end: (out) (optional) (transfer none) (array zero-terminated=1) (element-type guint8): return location for end of valid data
2230 : : *
2231 : : * Validates UTF-8 encoded text.
2232 : : *
2233 : : * @str is the text to validate; if @str is nul-terminated, then @max_len can be
2234 : : * `-1`, otherwise @max_len should be the number of bytes to validate.
2235 : : *
2236 : : * If @end is non-`NULL`, then the end of the valid range will be stored there.
2237 : : * This is the first byte of the first invalid character if some bytes were
2238 : : * invalid, or the end of the text being validated otherwise — either the
2239 : : * trailing nul byte, or the first byte beyond @max_len (if it’s positive).
2240 : : *
2241 : : * Note that `g_utf8_validate()` returns `FALSE` if @max_len is positive and
2242 : : * any of the @max_len bytes are nul.
2243 : : *
2244 : : * Returns `TRUE` if all of @str was valid. Many GLib and GTK
2245 : : * routines require valid UTF-8 as input; so data read from a file
2246 : : * or the network should be checked with `g_utf8_validate()` before
2247 : : * doing anything else with it.
2248 : : *
2249 : : * Returns: `TRUE` if the text was valid UTF-8
2250 : : */
2251 : : gboolean
2252 : 1332632 : g_utf8_validate (const char *str,
2253 : : gssize max_len,
2254 : : const gchar **end)
2255 : : {
2256 : 1332632 : size_t max_len_unsigned = (max_len >= 0) ? (size_t) max_len : strlen (str);
2257 : :
2258 : 1332632 : return g_utf8_validate_len (str, max_len_unsigned, end);
2259 : : }
2260 : :
2261 : : /**
2262 : : * g_utf8_validate_len:
2263 : : * @str: (array length=max_len) (element-type guint8): a pointer to character data
2264 : : * @max_len: max bytes to validate
2265 : : * @end: (out) (optional) (transfer none) (array zero-terminated=1) (element-type guint8): return location for end of valid data
2266 : : *
2267 : : * Validates UTF-8 encoded text.
2268 : : *
2269 : : * As with [func@GLib.utf8_validate], but @max_len must be set, and hence this
2270 : : * function will always return `FALSE` if any of the bytes of @str are nul.
2271 : : *
2272 : : * Returns: `TRUE` if the text was valid UTF-8
2273 : : * Since: 2.60
2274 : : */
2275 : : gboolean
2276 : 2743232 : g_utf8_validate_len (const char *str,
2277 : : gsize max_len,
2278 : : const gchar **end)
2279 : :
2280 : : {
2281 : 2743232 : utf8_verify (&str, &max_len);
2282 : :
2283 : 2743232 : if (end != NULL)
2284 : 2706239 : *end = str;
2285 : :
2286 : 2743232 : return max_len == 0;
2287 : : }
2288 : :
2289 : : /**
2290 : : * g_str_is_ascii:
2291 : : * @str: a string
2292 : : *
2293 : : * Determines if a string is pure ASCII. A string is pure ASCII if it
2294 : : * contains no bytes with the high bit set.
2295 : : *
2296 : : * Returns: true if @str is ASCII
2297 : : *
2298 : : * Since: 2.40
2299 : : */
2300 : : gboolean
2301 : 2479 : g_str_is_ascii (const gchar *str)
2302 : : {
2303 : 2479 : utf8_verify_ascii (&str, NULL);
2304 : :
2305 : 2479 : return *str == 0;
2306 : : }
2307 : :
2308 : : /**
2309 : : * g_unichar_validate:
2310 : : * @ch: a Unicode character
2311 : : *
2312 : : * Checks whether @ch is a valid Unicode character.
2313 : : *
2314 : : * Some possible integer values of @ch will not be valid. U+0000 is considered a
2315 : : * valid character, though it’s normally a string terminator.
2316 : : *
2317 : : * Returns: `TRUE` if @ch is a valid Unicode character
2318 : : **/
2319 : : gboolean
2320 : 10 : g_unichar_validate (gunichar ch)
2321 : : {
2322 : 10 : return UNICODE_VALID (ch);
2323 : : }
2324 : :
2325 : : /**
2326 : : * g_utf8_strreverse:
2327 : : * @str: a UTF-8 encoded string
2328 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
2329 : : * then the string is nul-terminated.
2330 : : *
2331 : : * Reverses a UTF-8 string.
2332 : : *
2333 : : * @str must be valid UTF-8 encoded text. (Use [func@GLib.utf8_validate] on all
2334 : : * text before trying to use UTF-8 utility functions with it.)
2335 : : *
2336 : : * This function is intended for programmatic uses of reversed strings.
2337 : : * It pays no attention to decomposed characters, combining marks, byte
2338 : : * order marks, directional indicators (LRM, LRO, etc) and similar
2339 : : * characters which might need special handling when reversing a string
2340 : : * for display purposes.
2341 : : *
2342 : : * Note that unlike [func@GLib.strreverse], this function returns
2343 : : * newly-allocated memory, which should be freed with [func@GLib.free] when
2344 : : * no longer needed.
2345 : : *
2346 : : * Returns: (transfer full): a newly-allocated string which is the reverse of @str
2347 : : *
2348 : : * Since: 2.2
2349 : : */
2350 : : gchar *
2351 : 232 : g_utf8_strreverse (const gchar *str,
2352 : : gssize len)
2353 : : {
2354 : : gchar *r, *result;
2355 : : const gchar *p;
2356 : :
2357 : 232 : if (len < 0)
2358 : 53 : len = strlen (str);
2359 : :
2360 : 232 : result = g_new (gchar, len + 1);
2361 : 232 : r = result + len;
2362 : 232 : p = str;
2363 : 4175 : while (r > result)
2364 : : {
2365 : 3943 : gchar *m, skip = g_utf8_skip[*(guchar*) p];
2366 : 3943 : r -= skip;
2367 : 3943 : g_assert (r >= result);
2368 : 7916 : for (m = r; skip; skip--)
2369 : 3973 : *m++ = *p++;
2370 : : }
2371 : 232 : result[len] = 0;
2372 : :
2373 : 232 : return result;
2374 : : }
2375 : :
2376 : : /**
2377 : : * g_utf8_make_valid:
2378 : : * @str: string to coerce into UTF-8
2379 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
2380 : : * then the string is nul-terminated.
2381 : : *
2382 : : * If the provided string is valid UTF-8, return a copy of it. If not,
2383 : : * return a copy in which bytes that could not be interpreted as valid Unicode
2384 : : * are replaced with the Unicode replacement character (U+FFFD).
2385 : : *
2386 : : * For example, this is an appropriate function to use if you have received
2387 : : * a string that was incorrectly declared to be UTF-8, and you need a valid
2388 : : * UTF-8 version of it that can be logged or displayed to the user, with the
2389 : : * assumption that it is close enough to ASCII or UTF-8 to be mostly
2390 : : * readable as-is.
2391 : : *
2392 : : * Returns: (transfer full): a valid UTF-8 string whose content resembles @str
2393 : : *
2394 : : * Since: 2.52
2395 : : */
2396 : : gchar *
2397 : 310 : g_utf8_make_valid (const gchar *str,
2398 : : gssize len)
2399 : : {
2400 : : GString *string;
2401 : : const gchar *remainder, *invalid;
2402 : : gsize remaining_bytes, valid_bytes;
2403 : :
2404 : 310 : g_return_val_if_fail (str != NULL, NULL);
2405 : :
2406 : 310 : if (len < 0)
2407 : 299 : len = strlen (str);
2408 : :
2409 : 310 : string = NULL;
2410 : 310 : remainder = str;
2411 : 310 : remaining_bytes = len;
2412 : :
2413 : 380 : while (remaining_bytes != 0)
2414 : : {
2415 : 359 : if (g_utf8_validate (remainder, remaining_bytes, &invalid))
2416 : 289 : break;
2417 : 70 : valid_bytes = invalid - remainder;
2418 : :
2419 : 70 : if (string == NULL)
2420 : 40 : string = g_string_sized_new (remaining_bytes);
2421 : :
2422 : 70 : g_string_append_len (string, remainder, valid_bytes);
2423 : : /* append U+FFFD REPLACEMENT CHARACTER */
2424 : 70 : g_string_append (string, "\357\277\275");
2425 : :
2426 : 70 : remaining_bytes -= valid_bytes + 1;
2427 : 70 : remainder = invalid + 1;
2428 : : }
2429 : :
2430 : 310 : if (string == NULL)
2431 : 270 : return g_strndup (str, len);
2432 : :
2433 : 40 : g_string_append_len (string, remainder, remaining_bytes);
2434 : : g_string_append_c (string, '\0');
2435 : :
2436 : 40 : g_assert (g_utf8_validate (string->str, -1, NULL));
2437 : :
2438 : 40 : return g_string_free (string, FALSE);
2439 : : }
|