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