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 : :
31 : : #ifdef G_PLATFORM_WIN32
32 : : #include <stdio.h>
33 : : #include <windows.h>
34 : : #endif
35 : :
36 : : #include "gconvert.h"
37 : : #include "ghash.h"
38 : : #include "gstrfuncs.h"
39 : : #include "gtestutils.h"
40 : : #include "gtypes.h"
41 : : #include "gthread.h"
42 : : #include "glibintl.h"
43 : : #include "gvalgrind.h"
44 : :
45 : : #define UTF8_COMPUTE(Char, Mask, Len) \
46 : : if (Char < 128) \
47 : : { \
48 : : Len = 1; \
49 : : Mask = 0x7f; \
50 : : } \
51 : : else if ((Char & 0xe0) == 0xc0) \
52 : : { \
53 : : Len = 2; \
54 : : Mask = 0x1f; \
55 : : } \
56 : : else if ((Char & 0xf0) == 0xe0) \
57 : : { \
58 : : Len = 3; \
59 : : Mask = 0x0f; \
60 : : } \
61 : : else if ((Char & 0xf8) == 0xf0) \
62 : : { \
63 : : Len = 4; \
64 : : Mask = 0x07; \
65 : : } \
66 : : else if ((Char & 0xfc) == 0xf8) \
67 : : { \
68 : : Len = 5; \
69 : : Mask = 0x03; \
70 : : } \
71 : : else if ((Char & 0xfe) == 0xfc) \
72 : : { \
73 : : Len = 6; \
74 : : Mask = 0x01; \
75 : : } \
76 : : else \
77 : : Len = -1;
78 : :
79 : : #define UTF8_LENGTH(Char) \
80 : : ((Char) < 0x80 ? 1 : \
81 : : ((Char) < 0x800 ? 2 : \
82 : : ((Char) < 0x10000 ? 3 : \
83 : : ((Char) < 0x200000 ? 4 : \
84 : : ((Char) < 0x4000000 ? 5 : 6)))))
85 : :
86 : :
87 : : #define UTF8_GET(Result, Chars, Count, Mask, Len) \
88 : : (Result) = (Chars)[0] & (Mask); \
89 : : for ((Count) = 1; (Count) < (Len); ++(Count)) \
90 : : { \
91 : : if (((Chars)[(Count)] & 0xc0) != 0x80) \
92 : : { \
93 : : (Result) = -1; \
94 : : break; \
95 : : } \
96 : : (Result) <<= 6; \
97 : : (Result) |= ((Chars)[(Count)] & 0x3f); \
98 : : }
99 : :
100 : : /*
101 : : * Check whether a Unicode (5.2) char is in a valid range.
102 : : *
103 : : * The first check comes from the Unicode guarantee to never encode
104 : : * a point above 0x0010ffff, since UTF-16 couldn't represent it.
105 : : *
106 : : * The second check covers surrogate pairs (category Cs).
107 : : *
108 : : * @param Char the character
109 : : */
110 : : #define UNICODE_VALID(Char) \
111 : : ((Char) < 0x110000 && \
112 : : (((Char) & 0xFFFFF800) != 0xD800))
113 : :
114 : :
115 : : static const gchar utf8_skip_data[256] = {
116 : : 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,
117 : : 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,
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 : : 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,
123 : : 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
124 : : };
125 : :
126 : : const gchar * const g_utf8_skip = utf8_skip_data;
127 : :
128 : : /**
129 : : * g_utf8_find_prev_char:
130 : : * @str: pointer to the beginning of a UTF-8 encoded string
131 : : * @p: pointer to some position within @str
132 : : *
133 : : * Given a position @p with a UTF-8 encoded string @str, find the start
134 : : * of the previous UTF-8 character starting before @p. Returns `NULL` if no
135 : : * UTF-8 characters are present in @str before @p.
136 : : *
137 : : * @p does not have to be at the beginning of a UTF-8 character. No check
138 : : * is made to see if the character found is actually valid other than
139 : : * it starts with an appropriate byte.
140 : : *
141 : : * Returns: (transfer none) (nullable): a pointer to the found character
142 : : */
143 : : gchar *
144 : 30 : g_utf8_find_prev_char (const gchar *str,
145 : : const gchar *p)
146 : : {
147 : 54 : while (p > str)
148 : : {
149 : 51 : --p;
150 : 51 : if ((*p & 0xc0) != 0x80)
151 : 27 : return (gchar *)p;
152 : : }
153 : 3 : return NULL;
154 : : }
155 : :
156 : : /**
157 : : * g_utf8_find_next_char:
158 : : * @p: a pointer to a position within a UTF-8 encoded string
159 : : * @end: (nullable): a pointer to the byte following the end of the string,
160 : : * or `NULL` to indicate that the string is nul-terminated
161 : : *
162 : : * Finds the start of the next UTF-8 character in the string after @p.
163 : : *
164 : : * @p does not have to be at the beginning of a UTF-8 character. No check
165 : : * is made to see if the character found is actually valid other than
166 : : * it starts with an appropriate byte.
167 : : *
168 : : * If @end is `NULL`, the return value will never be `NULL`: if the end of the
169 : : * string is reached, a pointer to the terminating nul byte is returned. If
170 : : * @end is non-`NULL`, the return value will be `NULL` if the end of the string
171 : : * is reached.
172 : : *
173 : : * Returns: (transfer none) (nullable): a pointer to the found character or `NULL` if @end is
174 : : * set and is reached
175 : : */
176 : : gchar *
177 : 631653 : g_utf8_find_next_char (const gchar *p,
178 : : const gchar *end)
179 : : {
180 : 631653 : if (end)
181 : : {
182 : 561285 : for (++p; p < end && (*p & 0xc0) == 0x80; ++p)
183 : : ;
184 : 561277 : return (p >= end) ? NULL : (gchar *)p;
185 : : }
186 : : else
187 : : {
188 : 70390 : for (++p; (*p & 0xc0) == 0x80; ++p)
189 : : ;
190 : 70376 : return (gchar *)p;
191 : : }
192 : : }
193 : :
194 : : /**
195 : : * g_utf8_prev_char:
196 : : * @p: a pointer to a position within a UTF-8 encoded string
197 : : *
198 : : * Finds the previous UTF-8 character in the string before @p.
199 : : *
200 : : * @p does not have to be at the beginning of a UTF-8 character. No check
201 : : * is made to see if the character found is actually valid other than
202 : : * it starts with an appropriate byte. If @p might be the first
203 : : * character of the string, you must use [func@GLib.utf8_find_prev_char]
204 : : * instead.
205 : : *
206 : : * Returns: (transfer none) (not nullable): a pointer to the found character
207 : : */
208 : : gchar *
209 : 282 : g_utf8_prev_char (const gchar *p)
210 : : {
211 : : while (TRUE)
212 : : {
213 : 465 : p--;
214 : 465 : if ((*p & 0xc0) != 0x80)
215 : 282 : return (gchar *)p;
216 : : }
217 : : }
218 : :
219 : : /**
220 : : * g_utf8_strlen:
221 : : * @p: pointer to the start of a UTF-8 encoded string
222 : : * @max: the maximum number of bytes to examine. If @max
223 : : * is less than 0, then the string is assumed to be
224 : : * nul-terminated. If @max is 0, @p will not be examined and
225 : : * may be `NULL`. If @max is greater than 0, up to @max
226 : : * bytes are examined
227 : : *
228 : : * Computes the length of the string in characters, not including
229 : : * the terminating nul character. If the @max’th byte falls in the
230 : : * middle of a character, the last (partial) character is not counted.
231 : : *
232 : : * Returns: the length of the string in characters
233 : : */
234 : : glong
235 : 63282 : g_utf8_strlen (const gchar *p,
236 : : gssize max)
237 : : {
238 : 63282 : glong len = 0;
239 : 63282 : const gchar *start = p;
240 : 63282 : g_return_val_if_fail (p != NULL || max == 0, 0);
241 : :
242 : 63282 : if (max < 0)
243 : : {
244 : 184144 : while (*p)
245 : : {
246 : 120887 : p = g_utf8_next_char (p);
247 : 120887 : ++len;
248 : : }
249 : : }
250 : : else
251 : : {
252 : 25 : if (max == 0 || !*p)
253 : 3 : return 0;
254 : :
255 : 22 : p = g_utf8_next_char (p);
256 : :
257 : 1579 : while (p - start < max && *p)
258 : : {
259 : 1557 : ++len;
260 : 1557 : p = g_utf8_next_char (p);
261 : : }
262 : :
263 : : /* only do the last len increment if we got a complete
264 : : * char (don't count partial chars)
265 : : */
266 : 22 : if (p - start <= max)
267 : 16 : ++len;
268 : : }
269 : :
270 : 63279 : return len;
271 : : }
272 : :
273 : : /**
274 : : * g_utf8_substring:
275 : : * @str: a UTF-8 encoded string
276 : : * @start_pos: a character offset within @str
277 : : * @end_pos: another character offset within @str,
278 : : * or `-1` to indicate the end of the string
279 : : *
280 : : * Copies a substring out of a UTF-8 encoded string.
281 : : * The substring will contain @end_pos - @start_pos characters.
282 : : *
283 : : * Since GLib 2.72, `-1` can be passed to @end_pos to indicate the
284 : : * end of the string.
285 : : *
286 : : * Returns: (transfer full): a newly allocated copy of the requested
287 : : * substring. Free with [func@GLib.free] when no longer needed.
288 : : *
289 : : * Since: 2.30
290 : : */
291 : : gchar *
292 : 5 : g_utf8_substring (const gchar *str,
293 : : glong start_pos,
294 : : glong end_pos)
295 : : {
296 : : gchar *start, *end, *out;
297 : :
298 : 5 : g_return_val_if_fail (end_pos >= start_pos || end_pos == -1, NULL);
299 : :
300 : 5 : start = g_utf8_offset_to_pointer (str, start_pos);
301 : :
302 : 5 : if (end_pos == -1)
303 : : {
304 : 1 : glong length = g_utf8_strlen (start, -1);
305 : 1 : end = g_utf8_offset_to_pointer (start, length);
306 : : }
307 : : else
308 : : {
309 : 4 : end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
310 : : }
311 : :
312 : 5 : out = g_malloc (end - start + 1);
313 : 5 : memcpy (out, start, end - start);
314 : 5 : out[end - start] = 0;
315 : :
316 : 5 : return out;
317 : : }
318 : :
319 : : /**
320 : : * g_utf8_get_char:
321 : : * @p: a pointer to Unicode character encoded as UTF-8
322 : : *
323 : : * Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
324 : : *
325 : : * If @p does not point to a valid UTF-8 encoded character, results
326 : : * are undefined. If you are not sure that the bytes are complete
327 : : * valid Unicode characters, you should use [func@GLib.utf8_get_char_validated]
328 : : * instead.
329 : : *
330 : : * Returns: the resulting character
331 : : */
332 : : gunichar
333 : 9617848 : g_utf8_get_char (const gchar *p)
334 : : {
335 : 9617848 : int i, mask = 0, len;
336 : : gunichar result;
337 : 9617848 : unsigned char c = (unsigned char) *p;
338 : :
339 : 9617848 : UTF8_COMPUTE (c, mask, len);
340 : 9617848 : if (len == -1)
341 : 10 : return (gunichar)-1;
342 : 12401923 : UTF8_GET (result, p, i, mask, len);
343 : :
344 : 9617838 : return result;
345 : : }
346 : :
347 : : /**
348 : : * g_utf8_offset_to_pointer:
349 : : * @str: a UTF-8 encoded string
350 : : * @offset: a character offset within @str
351 : : *
352 : : * Converts from an integer character offset to a pointer to a position
353 : : * within the string.
354 : : *
355 : : * Since 2.10, this function allows to pass a negative @offset to
356 : : * step backwards. It is usually worth stepping backwards from the end
357 : : * instead of forwards if @offset is in the last fourth of the string,
358 : : * since moving forward is about 3 times faster than moving backward.
359 : : *
360 : : * Note that this function doesn’t abort when reaching the end of @str.
361 : : * Therefore you should be sure that @offset is within string boundaries
362 : : * before calling that function. Call [func@GLib.utf8_strlen] when unsure.
363 : : * This limitation exists as this function is called frequently during
364 : : * text rendering and therefore has to be as fast as possible.
365 : : *
366 : : * Returns: (transfer none): the resulting pointer
367 : : */
368 : : gchar *
369 : 580726 : g_utf8_offset_to_pointer (const gchar *str,
370 : : glong offset)
371 : : {
372 : 580726 : const gchar *s = str;
373 : :
374 : 580726 : if (offset > 0)
375 : 74032020 : while (offset--)
376 : 73742005 : s = g_utf8_next_char (s);
377 : : else
378 : : {
379 : : const char *s1;
380 : :
381 : : /* This nice technique for fast backwards stepping
382 : : * through a UTF-8 string was dubbed "stutter stepping"
383 : : * by its inventor, Larry Ewing.
384 : : */
385 : 954612 : while (offset)
386 : : {
387 : 663901 : s1 = s;
388 : 663901 : s += offset;
389 : 764185 : while ((*s & 0xc0) == 0x80)
390 : 100284 : s--;
391 : :
392 : 663901 : offset += g_utf8_pointer_to_offset (s, s1);
393 : : }
394 : : }
395 : :
396 : 580726 : return (gchar *)s;
397 : : }
398 : :
399 : : /**
400 : : * g_utf8_pointer_to_offset:
401 : : * @str: a UTF-8 encoded string
402 : : * @pos: a pointer to a position within @str
403 : : *
404 : : * Converts from a pointer to position within a string to an integer
405 : : * character offset.
406 : : *
407 : : * Since 2.10, this function allows @pos to be before @str, and returns
408 : : * a negative offset in this case.
409 : : *
410 : : * Returns: the resulting character offset
411 : : */
412 : : glong
413 : 1534544 : g_utf8_pointer_to_offset (const gchar *str,
414 : : const gchar *pos)
415 : : {
416 : 1534544 : const gchar *s = str;
417 : 1534544 : glong offset = 0;
418 : :
419 : 1534544 : if (pos < str)
420 : 289941 : offset = - g_utf8_pointer_to_offset (pos, str);
421 : : else
422 : 222470149 : while (s < pos)
423 : : {
424 : 221225546 : s = g_utf8_next_char (s);
425 : 221225546 : offset++;
426 : : }
427 : :
428 : 1534544 : return offset;
429 : : }
430 : :
431 : :
432 : : /**
433 : : * g_utf8_strncpy:
434 : : * @dest: (transfer none): buffer to fill with characters from @src
435 : : * @src: UTF-8 encoded string
436 : : * @n: character count
437 : : *
438 : : * Like the standard C [`strncpy()`](man:strncpy) function, but copies a given
439 : : * number of characters instead of a given number of bytes.
440 : : *
441 : : * The @src string must be valid UTF-8 encoded text. (Use
442 : : * [func@GLib.utf8_validate] on all text before trying to use UTF-8 utility
443 : : * functions with it.)
444 : : *
445 : : * Note you must ensure @dest is at least 4 * @n + 1 to fit the
446 : : * largest possible UTF-8 characters
447 : : *
448 : : * Returns: (transfer none): @dest
449 : : */
450 : : gchar *
451 : 8 : g_utf8_strncpy (gchar *dest,
452 : : const gchar *src,
453 : : gsize n)
454 : : {
455 : 8 : const gchar *s = src;
456 : 35 : while (n && *s)
457 : : {
458 : 27 : s = g_utf8_next_char(s);
459 : 27 : n--;
460 : : }
461 : 8 : strncpy(dest, src, s - src);
462 : 8 : dest[s - src] = 0;
463 : 8 : return dest;
464 : : }
465 : :
466 : : /**
467 : : * g_utf8_truncate_middle:
468 : : * @string: (transfer none): a nul-terminated UTF-8 encoded string
469 : : * @truncate_length: the new size of @string, in characters, including the ellipsis character
470 : : *
471 : : * Cuts off the middle of the string, preserving half of @truncate_length
472 : : * characters at the beginning and half at the end.
473 : : *
474 : : * If @string is already short enough, this returns a copy of @string.
475 : : * If @truncate_length is `0`, an empty string is returned.
476 : : *
477 : : * Returns: (transfer full): a newly-allocated copy of @string ellipsized in the middle
478 : : *
479 : : * Since: 2.78
480 : : */
481 : : gchar *
482 : 34 : g_utf8_truncate_middle (const gchar *string,
483 : : gsize truncate_length)
484 : : {
485 : 34 : const gchar *ellipsis = "…";
486 : 34 : const gsize ellipsis_bytes = strlen (ellipsis);
487 : :
488 : : gsize length;
489 : : gsize left_substring_length;
490 : : gchar *left_substring_end;
491 : : gchar *right_substring_begin;
492 : : gchar *right_substring_end;
493 : : gsize left_bytes;
494 : : gsize right_bytes;
495 : : gchar *result;
496 : :
497 : 34 : g_return_val_if_fail (string != NULL, NULL);
498 : :
499 : 34 : length = g_utf8_strlen (string, -1);
500 : : /* Current string already smaller than requested length */
501 : 34 : if (length <= truncate_length)
502 : 8 : return g_strdup (string);
503 : 26 : if (truncate_length == 0)
504 : 2 : return g_strdup ("");
505 : :
506 : : /* Find substrings to keep, ignore ellipsis character for that */
507 : 24 : truncate_length -= 1;
508 : :
509 : 24 : left_substring_length = truncate_length / 2;
510 : :
511 : 24 : left_substring_end = g_utf8_offset_to_pointer (string, left_substring_length);
512 : 24 : right_substring_begin = g_utf8_offset_to_pointer (left_substring_end,
513 : 24 : length - truncate_length);
514 : 24 : right_substring_end = g_utf8_offset_to_pointer (right_substring_begin,
515 : 24 : truncate_length - left_substring_length);
516 : :
517 : 24 : g_assert (*right_substring_end == '\0');
518 : :
519 : 24 : left_bytes = left_substring_end - string;
520 : 24 : right_bytes = right_substring_end - right_substring_begin;
521 : :
522 : 24 : result = g_malloc (left_bytes + ellipsis_bytes + right_bytes + 1);
523 : :
524 : 24 : strncpy (result, string, left_bytes);
525 : 24 : memcpy (result + left_bytes, ellipsis, ellipsis_bytes);
526 : 24 : strncpy (result + left_bytes + ellipsis_bytes, right_substring_begin, right_bytes);
527 : 24 : result[left_bytes + ellipsis_bytes + right_bytes] = '\0';
528 : :
529 : 24 : return result;
530 : : }
531 : :
532 : : /* unicode_strchr */
533 : :
534 : : /**
535 : : * g_unichar_to_utf8:
536 : : * @c: a Unicode character code
537 : : * @outbuf: (out caller-allocates) (optional): output buffer, must have at
538 : : * least 6 bytes of space. If `NULL`, the length will be computed and
539 : : * returned and nothing will be written to @outbuf.
540 : : *
541 : : * Converts a single character to UTF-8.
542 : : *
543 : : * Returns: number of bytes written
544 : : */
545 : : int
546 : 953109 : g_unichar_to_utf8 (gunichar c,
547 : : gchar *outbuf)
548 : : {
549 : : /* If this gets modified, also update the copy in g_string_insert_unichar() */
550 : 953109 : guint len = 0;
551 : : int first;
552 : : int i;
553 : :
554 : 953109 : if (c < 0x80)
555 : : {
556 : 183189 : first = 0;
557 : 183189 : len = 1;
558 : : }
559 : 769920 : else if (c < 0x800)
560 : : {
561 : 179239 : first = 0xc0;
562 : 179239 : len = 2;
563 : : }
564 : 590681 : else if (c < 0x10000)
565 : : {
566 : 558852 : first = 0xe0;
567 : 558852 : len = 3;
568 : : }
569 : 31829 : else if (c < 0x200000)
570 : : {
571 : 31829 : first = 0xf0;
572 : 31829 : len = 4;
573 : : }
574 : 0 : else if (c < 0x4000000)
575 : : {
576 : 0 : first = 0xf8;
577 : 0 : len = 5;
578 : : }
579 : : else
580 : : {
581 : 0 : first = 0xfc;
582 : 0 : len = 6;
583 : : }
584 : :
585 : 953109 : if (outbuf)
586 : : {
587 : 2332736 : for (i = len - 1; i > 0; --i)
588 : : {
589 : 1383848 : outbuf[i] = (c & 0x3f) | 0x80;
590 : 1383848 : c >>= 6;
591 : : }
592 : 948888 : outbuf[0] = c | first;
593 : : }
594 : :
595 : 953109 : return len;
596 : : }
597 : :
598 : : /**
599 : : * g_utf8_strchr:
600 : : * @p: a nul-terminated UTF-8 encoded string
601 : : * @len: the maximum length of @p
602 : : * @c: a Unicode character
603 : : *
604 : : * Finds the leftmost occurrence of the given Unicode character
605 : : * in a UTF-8 encoded string, while limiting the search to @len bytes.
606 : : *
607 : : * If @len is `-1`, allow unbounded search.
608 : : *
609 : : * Returns: (transfer none) (nullable): `NULL` if the string does not contain
610 : : * the character, otherwise, a pointer to the start of the leftmost occurrence
611 : : * of the character in the string.
612 : : */
613 : : gchar *
614 : 43457 : g_utf8_strchr (const char *p,
615 : : gssize len,
616 : : gunichar c)
617 : : {
618 : : gchar ch[10];
619 : :
620 : 43457 : gint charlen = g_unichar_to_utf8 (c, ch);
621 : 43457 : ch[charlen] = '\0';
622 : :
623 : 43457 : return g_strstr_len (p, len, ch);
624 : : }
625 : :
626 : :
627 : : /**
628 : : * g_utf8_strrchr:
629 : : * @p: a nul-terminated UTF-8 encoded string
630 : : * @len: the maximum length of @p
631 : : * @c: a Unicode character
632 : : *
633 : : * Find the rightmost occurrence of the given Unicode character
634 : : * in a UTF-8 encoded string, while limiting the search to @len bytes.
635 : : *
636 : : * If @len is `-1`, allow unbounded search.
637 : : *
638 : : * Returns: (transfer none) (nullable): `NULL` if the string does not contain
639 : : * the character, otherwise, a pointer to the start of the rightmost
640 : : * occurrence of the character in the string.
641 : : */
642 : : gchar *
643 : 5 : g_utf8_strrchr (const char *p,
644 : : gssize len,
645 : : gunichar c)
646 : : {
647 : : gchar ch[10];
648 : :
649 : 5 : gint charlen = g_unichar_to_utf8 (c, ch);
650 : 5 : ch[charlen] = '\0';
651 : :
652 : 5 : return g_strrstr_len (p, len, ch);
653 : : }
654 : :
655 : :
656 : : /* Like g_utf8_get_char, but take a maximum length
657 : : * and return (gunichar)-2 on incomplete trailing character;
658 : : * also check for malformed or overlong sequences
659 : : * and return (gunichar)-1 in this case.
660 : : */
661 : : static inline gunichar
662 : 2513950 : g_utf8_get_char_extended (const gchar *p,
663 : : gssize max_len)
664 : : {
665 : : gsize i, len;
666 : : gunichar min_code;
667 : 2513950 : gunichar wc = (guchar) *p;
668 : 2513950 : const gunichar partial_sequence = (gunichar) -2;
669 : 2513950 : const gunichar malformed_sequence = (gunichar) -1;
670 : :
671 : 2513950 : if (wc < 0x80)
672 : : {
673 : 2288871 : return wc;
674 : : }
675 : 225079 : else if (G_UNLIKELY (wc < 0xc0))
676 : : {
677 : 0 : return malformed_sequence;
678 : : }
679 : 225079 : else if (wc < 0xe0)
680 : : {
681 : 40885 : len = 2;
682 : 40885 : wc &= 0x1f;
683 : 40885 : min_code = 1 << 7;
684 : : }
685 : 184194 : else if (wc < 0xf0)
686 : : {
687 : 174406 : len = 3;
688 : 174406 : wc &= 0x0f;
689 : 174406 : min_code = 1 << 11;
690 : : }
691 : 9788 : else if (wc < 0xf8)
692 : : {
693 : 9751 : len = 4;
694 : 9751 : wc &= 0x07;
695 : 9751 : min_code = 1 << 16;
696 : : }
697 : 37 : else if (wc < 0xfc)
698 : : {
699 : 6 : len = 5;
700 : 6 : wc &= 0x03;
701 : 6 : min_code = 1 << 21;
702 : : }
703 : 31 : else if (wc < 0xfe)
704 : : {
705 : 31 : len = 6;
706 : 31 : wc &= 0x01;
707 : 31 : min_code = 1 << 26;
708 : : }
709 : : else
710 : : {
711 : 0 : return malformed_sequence;
712 : : }
713 : :
714 : 225079 : if (G_UNLIKELY (max_len >= 0 && len > (gsize) max_len))
715 : : {
716 : 627 : for (i = 1; i < (gsize) max_len; i++)
717 : : {
718 : 193 : if ((((guchar *)p)[i] & 0xc0) != 0x80)
719 : 1 : return malformed_sequence;
720 : : }
721 : 434 : return partial_sequence;
722 : : }
723 : :
724 : 642816 : for (i = 1; i < len; ++i)
725 : : {
726 : 418206 : gunichar ch = ((guchar *)p)[i];
727 : :
728 : 418206 : if (G_UNLIKELY ((ch & 0xc0) != 0x80))
729 : : {
730 : 34 : if (ch)
731 : 7 : return malformed_sequence;
732 : : else
733 : 27 : return partial_sequence;
734 : : }
735 : :
736 : 418172 : wc <<= 6;
737 : 418172 : wc |= (ch & 0x3f);
738 : : }
739 : :
740 : 224610 : if (G_UNLIKELY (wc < min_code))
741 : 0 : return malformed_sequence;
742 : :
743 : 224610 : return wc;
744 : : }
745 : :
746 : : /**
747 : : * g_utf8_get_char_validated:
748 : : * @p: a pointer to Unicode character encoded as UTF-8
749 : : * @max_len: the maximum number of bytes to read, or `-1` if @p is nul-terminated
750 : : *
751 : : * Convert a sequence of bytes encoded as UTF-8 to a Unicode character.
752 : : *
753 : : * This function checks for incomplete characters, for invalid characters
754 : : * such as characters that are out of the range of Unicode, and for
755 : : * overlong encodings of valid characters.
756 : : *
757 : : * Note that [func@GLib.utf8_get_char_validated] returns `(gunichar)-2` if
758 : : * @max_len is positive and any of the bytes in the first UTF-8 character
759 : : * sequence are nul.
760 : : *
761 : : * Returns: the resulting character. If @p points to a partial
762 : : * sequence at the end of a string that could begin a valid
763 : : * character (or if @max_len is zero), returns `(gunichar)-2`;
764 : : * otherwise, if @p does not point to a valid UTF-8 encoded
765 : : * Unicode character, returns `(gunichar)-1`.
766 : : */
767 : : gunichar
768 : 2508856 : g_utf8_get_char_validated (const gchar *p,
769 : : gssize max_len)
770 : : {
771 : : gunichar result;
772 : :
773 : 2508856 : if (max_len == 0)
774 : 1 : return (gunichar)-2;
775 : :
776 : 2508855 : result = g_utf8_get_char_extended (p, max_len);
777 : :
778 : : /* Disallow codepoint U+0000 as it’s a nul byte,
779 : : * and all string handling in GLib is nul-terminated */
780 : 2508855 : if (result == 0 && max_len > 0)
781 : 2 : return (gunichar) -2;
782 : :
783 : 2508853 : if (result & 0x80000000)
784 : 437 : return result;
785 : 2508416 : else if (!UNICODE_VALID (result))
786 : 0 : return (gunichar)-1;
787 : : else
788 : 2508416 : return result;
789 : : }
790 : :
791 : : #define CONT_BYTE_FAST(p) ((guchar)*p++ & 0x3f)
792 : :
793 : : /**
794 : : * g_utf8_to_ucs4_fast:
795 : : * @str: a UTF-8 encoded string
796 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
797 : : * then the string is nul-terminated.
798 : : * @items_written: (out) (optional): location to store the
799 : : * number of characters in the result, or `NULL`.
800 : : *
801 : : * Convert a string from UTF-8 to a 32-bit fixed width
802 : : * representation as UCS-4, assuming valid UTF-8 input.
803 : : *
804 : : * This function is roughly twice as fast as [func@GLib.utf8_to_ucs4]
805 : : * but does no error checking on the input. A trailing nul character (U+0000)
806 : : * will be added to the string after the converted text.
807 : : *
808 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
809 : : * This value must be freed with [func@GLib.free].
810 : : */
811 : : gunichar *
812 : 23 : g_utf8_to_ucs4_fast (const gchar *str,
813 : : glong len,
814 : : glong *items_written)
815 : : {
816 : : gunichar *result;
817 : : gint n_chars, i;
818 : : const gchar *p;
819 : :
820 : 23 : g_return_val_if_fail (str != NULL, NULL);
821 : :
822 : 23 : p = str;
823 : 23 : n_chars = 0;
824 : 23 : if (len < 0)
825 : : {
826 : 281 : while (*p)
827 : : {
828 : 264 : p = g_utf8_next_char (p);
829 : 264 : ++n_chars;
830 : : }
831 : : }
832 : : else
833 : : {
834 : 255 : while (p < str + len && *p)
835 : : {
836 : 249 : p = g_utf8_next_char (p);
837 : 249 : ++n_chars;
838 : : }
839 : : }
840 : :
841 : 23 : result = g_new (gunichar, n_chars + 1);
842 : :
843 : 23 : p = str;
844 : 536 : for (i=0; i < n_chars; i++)
845 : : {
846 : 513 : guchar first = (guchar)*p++;
847 : : gunichar wc;
848 : :
849 : 513 : if (first < 0xc0)
850 : : {
851 : : /* We really hope first < 0x80, but we don't want to test an
852 : : * extra branch for invalid input, which this function
853 : : * does not care about. Handling unexpected continuation bytes
854 : : * here will do the least damage. */
855 : 231 : wc = first;
856 : : }
857 : : else
858 : : {
859 : 282 : gunichar c1 = CONT_BYTE_FAST(p);
860 : 282 : if (first < 0xe0)
861 : : {
862 : 167 : wc = ((first & 0x1f) << 6) | c1;
863 : : }
864 : : else
865 : : {
866 : 115 : gunichar c2 = CONT_BYTE_FAST(p);
867 : 115 : if (first < 0xf0)
868 : : {
869 : 110 : wc = ((first & 0x0f) << 12) | (c1 << 6) | c2;
870 : : }
871 : : else
872 : : {
873 : 5 : gunichar c3 = CONT_BYTE_FAST(p);
874 : 5 : wc = ((first & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
875 : 5 : if (G_UNLIKELY (first >= 0xf8))
876 : : {
877 : : /* This can't be valid UTF-8, but g_utf8_next_char()
878 : : * and company allow out-of-range sequences */
879 : 0 : gunichar mask = 1 << 20;
880 : 0 : while ((wc & mask) != 0)
881 : : {
882 : 0 : wc <<= 6;
883 : 0 : wc |= CONT_BYTE_FAST(p);
884 : 0 : mask <<= 5;
885 : : }
886 : 0 : wc &= mask - 1;
887 : : }
888 : : }
889 : : }
890 : : }
891 : 513 : result[i] = wc;
892 : : }
893 : 23 : result[i] = 0;
894 : :
895 : 23 : if (items_written)
896 : 15 : *items_written = i;
897 : :
898 : 23 : return result;
899 : : }
900 : :
901 : : static gpointer
902 : 404224 : try_malloc_n (gsize n_blocks, gsize n_block_bytes, GError **error)
903 : : {
904 : 404224 : gpointer ptr = g_try_malloc_n (n_blocks, n_block_bytes);
905 : 404224 : if (ptr == NULL)
906 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_MEMORY,
907 : : _("Failed to allocate memory"));
908 : 404224 : return ptr;
909 : : }
910 : :
911 : : /**
912 : : * g_utf8_to_ucs4:
913 : : * @str: a UTF-8 encoded string
914 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
915 : : * then the string is nul-terminated.
916 : : * @items_read: (out) (optional): location to store number of
917 : : * bytes read, or `NULL`.
918 : : * If `NULL`, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
919 : : * returned in case @str contains a trailing partial
920 : : * character. If an error occurs then the index of the
921 : : * invalid input is stored here.
922 : : * @items_written: (out) (optional): location to store number
923 : : * of characters written or `NULL`. The value here stored does not include
924 : : * the trailing nul character.
925 : : * @error: location to store the error occurring, or `NULL` to ignore
926 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
927 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
928 : : *
929 : : * Convert a string from UTF-8 to a 32-bit fixed width representation as UCS-4.
930 : : *
931 : : * A trailing nul character (U+0000) will be added to the string after the
932 : : * converted text.
933 : : *
934 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
935 : : * This value must be freed with [func@GLib.free].
936 : : */
937 : : gunichar *
938 : 111 : g_utf8_to_ucs4 (const gchar *str,
939 : : glong len,
940 : : glong *items_read,
941 : : glong *items_written,
942 : : GError **error)
943 : : {
944 : 111 : gunichar *result = NULL;
945 : : gint n_chars, i;
946 : : const gchar *in;
947 : :
948 : 111 : in = str;
949 : 111 : n_chars = 0;
950 : 855 : while ((len < 0 || str + len - in > 0) && *in)
951 : : {
952 : 770 : gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
953 : 770 : if (wc & 0x80000000)
954 : : {
955 : 26 : if (wc == (gunichar)-2)
956 : : {
957 : 23 : if (items_read)
958 : 12 : break;
959 : : else
960 : 11 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
961 : : _("Partial character sequence at end of input"));
962 : : }
963 : : else
964 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
965 : : _("Invalid byte sequence in conversion input"));
966 : :
967 : 14 : goto err_out;
968 : : }
969 : :
970 : 744 : n_chars++;
971 : :
972 : 744 : in = g_utf8_next_char (in);
973 : : }
974 : :
975 : 97 : result = try_malloc_n (n_chars + 1, sizeof (gunichar), error);
976 : 97 : if (result == NULL)
977 : 0 : goto err_out;
978 : :
979 : 97 : in = str;
980 : 829 : for (i=0; i < n_chars; i++)
981 : : {
982 : 732 : result[i] = g_utf8_get_char (in);
983 : 732 : in = g_utf8_next_char (in);
984 : : }
985 : 97 : result[i] = 0;
986 : :
987 : 97 : if (items_written)
988 : 77 : *items_written = n_chars;
989 : :
990 : 20 : err_out:
991 : 111 : if (items_read)
992 : 52 : *items_read = in - str;
993 : :
994 : 111 : return result;
995 : : }
996 : :
997 : : /**
998 : : * g_ucs4_to_utf8:
999 : : * @str: (array length=len) (element-type gunichar): a UCS-4 encoded string
1000 : : * @len: the maximum length (number of characters) of @str to use.
1001 : : * If @len is negative, then the string is nul-terminated.
1002 : : * @items_read: (out) (optional): location to store number of
1003 : : * characters read, or `NULL`.
1004 : : * @items_written: (out) (optional): location to store number
1005 : : * of bytes written or `NULL`. The value here stored does not include the
1006 : : * trailing nul byte.
1007 : : * @error: location to store the error occurring, or %NULL to ignore
1008 : : * errors. Any of the errors in #GConvertError other than
1009 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1010 : : *
1011 : : * Convert a string from a 32-bit fixed width representation as UCS-4.
1012 : : * to UTF-8.
1013 : : *
1014 : : * The result will be terminated with a nul byte.
1015 : : *
1016 : : * Returns: (transfer full): a pointer to a newly allocated UTF-8 string.
1017 : : * This value must be freed with [func@GLib.free]. If an error occurs,
1018 : : * @items_read will be set to the position of the first invalid input
1019 : : * character.
1020 : : */
1021 : : gchar *
1022 : 403854 : g_ucs4_to_utf8 (const gunichar *str,
1023 : : glong len,
1024 : : glong *items_read,
1025 : : glong *items_written,
1026 : : GError **error)
1027 : : {
1028 : : gint result_length;
1029 : 403854 : gchar *result = NULL;
1030 : : gchar *p;
1031 : : gint i;
1032 : :
1033 : 403854 : result_length = 0;
1034 : 1303044 : for (i = 0; len < 0 || i < len ; i++)
1035 : : {
1036 : 1303040 : if (!str[i])
1037 : 403847 : break;
1038 : :
1039 : 899193 : if (str[i] >= 0x80000000)
1040 : : {
1041 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1042 : : _("Character out of range for UTF-8"));
1043 : 3 : goto err_out;
1044 : : }
1045 : :
1046 : 899190 : result_length += UTF8_LENGTH (str[i]);
1047 : : }
1048 : :
1049 : 403851 : result = try_malloc_n (result_length + 1, 1, error);
1050 : 403851 : if (result == NULL)
1051 : 0 : goto err_out;
1052 : :
1053 : 403851 : p = result;
1054 : :
1055 : 403851 : i = 0;
1056 : 1303035 : while (p < result + result_length)
1057 : 899184 : p += g_unichar_to_utf8 (str[i++], p);
1058 : :
1059 : 403851 : *p = '\0';
1060 : :
1061 : 403851 : if (items_written)
1062 : 17 : *items_written = p - result;
1063 : :
1064 : 403834 : err_out:
1065 : 403854 : if (items_read)
1066 : 19 : *items_read = i;
1067 : :
1068 : 403854 : return result;
1069 : : }
1070 : :
1071 : : #define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000)
1072 : :
1073 : : /**
1074 : : * g_utf16_to_utf8:
1075 : : * @str: (array length=len) (element-type guint16): a UTF-16 encoded string
1076 : : * @len: the maximum length (number of #gunichar2) of @str to use.
1077 : : * If @len is negative, then the string is nul-terminated.
1078 : : * @items_read: (out) (optional): location to store number of words read, or
1079 : : * `NULL`. If `NULL`, then [error@GLib.ConvertError.PARTIAL_INPUT] will
1080 : : * be returned in case @str contains a trailing partial character. If
1081 : : * an error occurs then the index of the invalid input is stored here.
1082 : : * It’s guaranteed to be non-negative.
1083 : : * @items_written: (out) (optional): location to store number
1084 : : * of bytes written, or `NULL`. The value stored here does not include the
1085 : : * trailing nul byte. It’s guaranteed to be non-negative.
1086 : : * @error: location to store the error occurring, or `NULL` to ignore
1087 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1088 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1089 : : *
1090 : : * Convert a string from UTF-16 to UTF-8.
1091 : : *
1092 : : * The result will be terminated with a nul byte.
1093 : : *
1094 : : * Note that the input is expected to be already in native endianness,
1095 : : * an initial byte-order-mark character is not handled specially.
1096 : : * [func@GLib.convert] can be used to convert a byte buffer of UTF-16 data of
1097 : : * ambiguous endianness.
1098 : : *
1099 : : * Further note that this function does not validate the result
1100 : : * string; it may (for example) include embedded nul characters. The only
1101 : : * validation done by this function is to ensure that the input can
1102 : : * be correctly interpreted as UTF-16, i.e. it doesn’t contain
1103 : : * unpaired surrogates or partial character sequences.
1104 : : *
1105 : : * Returns: (transfer full): a pointer to a newly allocated UTF-8 string.
1106 : : * This value must be freed with [func@GLib.free].
1107 : : **/
1108 : : gchar *
1109 : 140 : g_utf16_to_utf8 (const gunichar2 *str,
1110 : : glong len,
1111 : : glong *items_read,
1112 : : glong *items_written,
1113 : : GError **error)
1114 : : {
1115 : : /* This function and g_utf16_to_ucs4 are almost exactly identical -
1116 : : * The lines that differ are marked.
1117 : : */
1118 : : const gunichar2 *in;
1119 : : gchar *out;
1120 : 140 : gchar *result = NULL;
1121 : : gint n_bytes;
1122 : : gunichar high_surrogate;
1123 : :
1124 : 140 : g_return_val_if_fail (str != NULL, NULL);
1125 : :
1126 : 140 : n_bytes = 0;
1127 : 140 : in = str;
1128 : 140 : high_surrogate = 0;
1129 : 2010 : while ((len < 0 || in - str < len) && *in)
1130 : : {
1131 : 1873 : gunichar2 c = *in;
1132 : : gunichar wc;
1133 : :
1134 : 1873 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1135 : : {
1136 : 8 : if (high_surrogate)
1137 : : {
1138 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1139 : 5 : high_surrogate = 0;
1140 : : }
1141 : : else
1142 : : {
1143 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1144 : : _("Invalid sequence in conversion input"));
1145 : 3 : goto err_out;
1146 : : }
1147 : : }
1148 : : else
1149 : : {
1150 : 1865 : if (high_surrogate)
1151 : : {
1152 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1153 : : _("Invalid sequence in conversion input"));
1154 : 0 : goto err_out;
1155 : : }
1156 : :
1157 : 1865 : if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1158 : : {
1159 : 11 : high_surrogate = c;
1160 : 11 : goto next1;
1161 : : }
1162 : : else
1163 : 1854 : wc = c;
1164 : : }
1165 : :
1166 : : /********** DIFFERENT for UTF8/UCS4 **********/
1167 : 1859 : n_bytes += UTF8_LENGTH (wc);
1168 : :
1169 : 1870 : next1:
1170 : 1870 : in++;
1171 : : }
1172 : :
1173 : 137 : if (high_surrogate && !items_read)
1174 : : {
1175 : 4 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1176 : : _("Partial character sequence at end of input"));
1177 : 4 : goto err_out;
1178 : : }
1179 : :
1180 : : /* At this point, everything is valid, and we just need to convert
1181 : : */
1182 : : /********** DIFFERENT for UTF8/UCS4 **********/
1183 : 133 : result = try_malloc_n (n_bytes + 1, 1, error);
1184 : 133 : if (result == NULL)
1185 : 0 : goto err_out;
1186 : :
1187 : 133 : high_surrogate = 0;
1188 : 133 : out = result;
1189 : 133 : in = str;
1190 : 1986 : while (out < result + n_bytes)
1191 : : {
1192 : 1853 : gunichar2 c = *in;
1193 : : gunichar wc;
1194 : :
1195 : 1853 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1196 : : {
1197 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1198 : 5 : high_surrogate = 0;
1199 : : }
1200 : 1848 : else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1201 : : {
1202 : 5 : high_surrogate = c;
1203 : 5 : goto next2;
1204 : : }
1205 : : else
1206 : 1843 : wc = c;
1207 : :
1208 : : /********** DIFFERENT for UTF8/UCS4 **********/
1209 : 1848 : out += g_unichar_to_utf8 (wc, out);
1210 : :
1211 : 1853 : next2:
1212 : 1853 : in++;
1213 : : }
1214 : :
1215 : : /********** DIFFERENT for UTF8/UCS4 **********/
1216 : 133 : *out = '\0';
1217 : :
1218 : 133 : if (items_written)
1219 : : /********** DIFFERENT for UTF8/UCS4 **********/
1220 : 19 : *items_written = out - result;
1221 : :
1222 : 114 : err_out:
1223 : 140 : if (items_read)
1224 : 21 : *items_read = in - str;
1225 : :
1226 : 140 : return result;
1227 : : }
1228 : :
1229 : : /**
1230 : : * g_utf16_to_ucs4:
1231 : : * @str: (array length=len) (element-type guint16): a UTF-16 encoded string
1232 : : * @len: the maximum length (number of #gunichar2) of @str to use.
1233 : : * If @len is negative, then the string is nul-terminated.
1234 : : * @items_read: (out) (optional): location to store number of words read, or
1235 : : * `NULL`. If `NULL`, then [error@GLib.ConvertError.PARTIAL_INPUT] will be
1236 : : * returned in case @str contains a trailing partial character. If
1237 : : * an error occurs then the index of the invalid input is stored here.
1238 : : * @items_written: (out) (optional): location to store number
1239 : : * of characters written, or `NULL`. The value stored here does not include
1240 : : * the trailing nul character.
1241 : : * @error: location to store the error occurring, or `NULL` to ignore
1242 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1243 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1244 : : *
1245 : : * Convert a string from UTF-16 to UCS-4.
1246 : : *
1247 : : * The result will be nul-terminated.
1248 : : *
1249 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
1250 : : * This value must be freed with [func@GLib.free].
1251 : : */
1252 : : gunichar *
1253 : 25 : g_utf16_to_ucs4 (const gunichar2 *str,
1254 : : glong len,
1255 : : glong *items_read,
1256 : : glong *items_written,
1257 : : GError **error)
1258 : : {
1259 : : const gunichar2 *in;
1260 : : gchar *out;
1261 : 25 : gchar *result = NULL;
1262 : : size_t n_bytes;
1263 : : gunichar high_surrogate;
1264 : :
1265 : 25 : g_return_val_if_fail (str != NULL, NULL);
1266 : :
1267 : 25 : n_bytes = 0;
1268 : 25 : in = str;
1269 : 25 : high_surrogate = 0;
1270 : 84 : while ((len < 0 || in - str < len) && *in)
1271 : : {
1272 : 62 : gunichar2 c = *in;
1273 : :
1274 : 62 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1275 : : {
1276 : 8 : if (high_surrogate)
1277 : : {
1278 : 5 : high_surrogate = 0;
1279 : : }
1280 : : else
1281 : : {
1282 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1283 : : _("Invalid sequence in conversion input"));
1284 : 3 : goto err_out;
1285 : : }
1286 : : }
1287 : : else
1288 : : {
1289 : 54 : if (high_surrogate)
1290 : : {
1291 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1292 : : _("Invalid sequence in conversion input"));
1293 : 0 : goto err_out;
1294 : : }
1295 : :
1296 : 54 : if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1297 : : {
1298 : 8 : high_surrogate = c;
1299 : 8 : goto next1;
1300 : : }
1301 : : }
1302 : :
1303 : : /********** DIFFERENT for UTF8/UCS4 **********/
1304 : 51 : n_bytes += sizeof (gunichar);
1305 : :
1306 : 59 : next1:
1307 : 59 : in++;
1308 : : }
1309 : :
1310 : 22 : if (high_surrogate && !items_read)
1311 : : {
1312 : 1 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1313 : : _("Partial character sequence at end of input"));
1314 : 1 : goto err_out;
1315 : : }
1316 : :
1317 : : /* At this point, everything is valid, and we just need to convert
1318 : : */
1319 : : /********** DIFFERENT for UTF8/UCS4 **********/
1320 : 21 : result = try_malloc_n (n_bytes + 4, 1, error);
1321 : 21 : if (result == NULL)
1322 : 0 : goto err_out;
1323 : :
1324 : 21 : high_surrogate = 0;
1325 : 21 : out = result;
1326 : 21 : in = str;
1327 : 69 : while (out < result + n_bytes)
1328 : : {
1329 : 48 : gunichar2 c = *in;
1330 : : gunichar wc;
1331 : :
1332 : 48 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1333 : : {
1334 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1335 : 5 : high_surrogate = 0;
1336 : : }
1337 : 43 : else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1338 : : {
1339 : 5 : high_surrogate = c;
1340 : 5 : goto next2;
1341 : : }
1342 : : else
1343 : 38 : wc = c;
1344 : :
1345 : : /********** DIFFERENT for UTF8/UCS4 **********/
1346 : 43 : *(gunichar *)out = wc;
1347 : 43 : out += sizeof (gunichar);
1348 : :
1349 : 48 : next2:
1350 : 48 : in++;
1351 : : }
1352 : :
1353 : : /********** DIFFERENT for UTF8/UCS4 **********/
1354 : 21 : *(gunichar *)out = 0;
1355 : :
1356 : 21 : if (items_written)
1357 : : /********** DIFFERENT for UTF8/UCS4 **********/
1358 : 19 : *items_written = (out - result) / sizeof (gunichar);
1359 : :
1360 : 2 : err_out:
1361 : 25 : if (items_read)
1362 : 21 : *items_read = in - str;
1363 : :
1364 : 25 : return (gunichar *)result;
1365 : : }
1366 : :
1367 : : /**
1368 : : * g_utf8_to_utf16:
1369 : : * @str: a UTF-8 encoded string
1370 : : * @len: the maximum length (number of bytes) of @str to use.
1371 : : * If @len is negative, then the string is nul-terminated.
1372 : : * @items_read: (out) (optional): location to store number of bytes read, or
1373 : : * `NULL`. If `NULL`, then [error@GLib.ConvertError.PARTIAL_INPUT] will
1374 : : * be returned in case @str contains a trailing partial character. If
1375 : : * an error occurs then the index of the invalid input is stored here.
1376 : : * @items_written: (out) (optional): location to store number
1377 : : * of `gunichar2` written, or `NULL`. The value stored here does not include
1378 : : * the trailing nul.
1379 : : * @error: location to store the error occurring, or `NULL` to ignore
1380 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1381 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1382 : : *
1383 : : * Convert a string from UTF-8 to UTF-16.
1384 : : *
1385 : : * A nul character (U+0000) will be added to the result after the converted text.
1386 : : *
1387 : : * Returns: (transfer full): a pointer to a newly allocated UTF-16 string.
1388 : : * This value must be freed with [func@GLib.free].
1389 : : */
1390 : : gunichar2 *
1391 : 107 : g_utf8_to_utf16 (const gchar *str,
1392 : : glong len,
1393 : : glong *items_read,
1394 : : glong *items_written,
1395 : : GError **error)
1396 : : {
1397 : 107 : gunichar2 *result = NULL;
1398 : : gint n16;
1399 : : const gchar *in;
1400 : : gint i;
1401 : :
1402 : 107 : g_return_val_if_fail (str != NULL, NULL);
1403 : :
1404 : 107 : in = str;
1405 : 107 : n16 = 0;
1406 : 4426 : while ((len < 0 || str + len - in > 0) && *in)
1407 : : {
1408 : 4325 : gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
1409 : 4325 : if (wc & 0x80000000)
1410 : : {
1411 : 6 : if (wc == (gunichar)-2)
1412 : : {
1413 : 3 : if (items_read)
1414 : 2 : break;
1415 : : else
1416 : 1 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1417 : : _("Partial character sequence at end of input"));
1418 : : }
1419 : : else
1420 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1421 : : _("Invalid byte sequence in conversion input"));
1422 : :
1423 : 4 : goto err_out;
1424 : : }
1425 : :
1426 : 4319 : if (wc < 0xd800)
1427 : 4311 : n16 += 1;
1428 : 8 : else if (wc < 0xe000)
1429 : : {
1430 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1431 : : _("Invalid sequence in conversion input"));
1432 : :
1433 : 0 : goto err_out;
1434 : : }
1435 : 8 : else if (wc < 0x10000)
1436 : 3 : n16 += 1;
1437 : 5 : else if (wc < 0x110000)
1438 : 5 : n16 += 2;
1439 : : else
1440 : : {
1441 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1442 : : _("Character out of range for UTF-16"));
1443 : :
1444 : 0 : goto err_out;
1445 : : }
1446 : :
1447 : 4319 : in = g_utf8_next_char (in);
1448 : : }
1449 : :
1450 : 103 : result = try_malloc_n (n16 + 1, sizeof (gunichar2), error);
1451 : 103 : if (result == NULL)
1452 : 0 : goto err_out;
1453 : :
1454 : 103 : in = str;
1455 : 4410 : for (i = 0; i < n16;)
1456 : : {
1457 : 4307 : gunichar wc = g_utf8_get_char (in);
1458 : :
1459 : 4307 : if (wc < 0x10000)
1460 : : {
1461 : 4302 : result[i++] = wc;
1462 : : }
1463 : : else
1464 : : {
1465 : 5 : result[i++] = (wc - 0x10000) / 0x400 + 0xd800;
1466 : 5 : result[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
1467 : : }
1468 : :
1469 : 4307 : in = g_utf8_next_char (in);
1470 : : }
1471 : :
1472 : 103 : result[i] = 0;
1473 : :
1474 : 103 : if (items_written)
1475 : 19 : *items_written = n16;
1476 : :
1477 : 84 : err_out:
1478 : 107 : if (items_read)
1479 : 21 : *items_read = in - str;
1480 : :
1481 : 107 : return result;
1482 : : }
1483 : :
1484 : : /**
1485 : : * g_ucs4_to_utf16:
1486 : : * @str: (array length=len) (element-type gunichar): a UCS-4 encoded string
1487 : : * @len: the maximum length (number of characters) of @str to use.
1488 : : * If @len is negative, then the string is nul-terminated.
1489 : : * @items_read: (out) (optional): location to store number of
1490 : : * bytes read, or `NULL`. If an error occurs then the index of the invalid
1491 : : * input is stored here.
1492 : : * @items_written: (out) (optional): location to store number
1493 : : * of `gunichar2` written, or `NULL`. The value stored here does not include
1494 : : * the trailing nul.
1495 : : * @error: location to store the error occurring, or `NULL` to ignore
1496 : : * errors. Any of the errors in [error@GLib.ConvertError] other than
1497 : : * [error@GLib.ConvertError.NO_CONVERSION] may occur.
1498 : : *
1499 : : * Convert a string from UCS-4 to UTF-16.
1500 : : *
1501 : : * A nul character (U+0000) will be added to the result after the converted text.
1502 : : *
1503 : : * Returns: (transfer full): a pointer to a newly allocated UTF-16 string.
1504 : : * This value must be freed with [func@GLib.free].
1505 : : */
1506 : : gunichar2 *
1507 : 22 : g_ucs4_to_utf16 (const gunichar *str,
1508 : : glong len,
1509 : : glong *items_read,
1510 : : glong *items_written,
1511 : : GError **error)
1512 : : {
1513 : 22 : gunichar2 *result = NULL;
1514 : : gint n16;
1515 : : gint i, j;
1516 : :
1517 : 22 : n16 = 0;
1518 : 22 : i = 0;
1519 : 67 : while ((len < 0 || i < len) && str[i])
1520 : : {
1521 : 48 : gunichar wc = str[i];
1522 : :
1523 : 48 : if (wc < 0xd800)
1524 : 37 : n16 += 1;
1525 : 11 : else if (wc < 0xe000)
1526 : : {
1527 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1528 : : _("Invalid sequence in conversion input"));
1529 : :
1530 : 0 : goto err_out;
1531 : : }
1532 : 11 : else if (wc < 0x10000)
1533 : 3 : n16 += 1;
1534 : 8 : else if (wc < 0x110000)
1535 : 5 : n16 += 2;
1536 : : else
1537 : : {
1538 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1539 : : _("Character out of range for UTF-16"));
1540 : :
1541 : 3 : goto err_out;
1542 : : }
1543 : :
1544 : 45 : i++;
1545 : : }
1546 : :
1547 : 19 : result = try_malloc_n (n16 + 1, sizeof (gunichar2), error);
1548 : 19 : if (result == NULL)
1549 : 0 : goto err_out;
1550 : :
1551 : 58 : for (i = 0, j = 0; j < n16; i++)
1552 : : {
1553 : 39 : gunichar wc = str[i];
1554 : :
1555 : 39 : if (wc < 0x10000)
1556 : : {
1557 : 34 : result[j++] = wc;
1558 : : }
1559 : : else
1560 : : {
1561 : 5 : result[j++] = (wc - 0x10000) / 0x400 + 0xd800;
1562 : 5 : result[j++] = (wc - 0x10000) % 0x400 + 0xdc00;
1563 : : }
1564 : : }
1565 : 19 : result[j] = 0;
1566 : :
1567 : 19 : if (items_written)
1568 : 17 : *items_written = n16;
1569 : :
1570 : 2 : err_out:
1571 : 22 : if (items_read)
1572 : 19 : *items_read = i;
1573 : :
1574 : 22 : return result;
1575 : : }
1576 : :
1577 : : /* SIMD-based UTF-8 validation originates in the c-utf8 project from
1578 : : * https://github.com/c-util/c-utf8/ from the following authors:
1579 : : *
1580 : : * David Rheinsberg <david@readahead.eu>
1581 : : * Evgeny Vereshchagin <evvers@ya.ru>
1582 : : * Jan Engelhardt <jengelh@inai.de>
1583 : : * Tom Gundersen <teg@jklm.no>
1584 : : *
1585 : : * It has been adapted for portability and integration.
1586 : : * The original code is dual-licensed Apache-2.0 or LGPLv2.1+
1587 : : */
1588 : :
1589 : : #define align_to(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1))
1590 : :
1591 : : static inline guint8
1592 : 34898459 : load_u8 (gconstpointer memory,
1593 : : gsize offset)
1594 : : {
1595 : 34898459 : return ((const guint8 *)memory)[offset];
1596 : : }
1597 : :
1598 : : #if G_GNUC_CHECK_VERSION(4,8) || defined(__clang__)
1599 : : # define _attribute_aligned(n) __attribute__((aligned(n)))
1600 : : #elif defined(_MSC_VER)
1601 : : # define _attribute_aligned(n) __declspec(align(n))
1602 : : #else
1603 : : # define _attribute_aligned(n)
1604 : : #endif
1605 : :
1606 : : static inline gsize
1607 : 25200420 : load_word (gconstpointer memory,
1608 : : gsize offset)
1609 : : {
1610 : : #if GLIB_SIZEOF_VOID_P == 8
1611 : 25200420 : _attribute_aligned(8) const guint8 *m = ((const guint8 *)memory) + offset;
1612 : :
1613 : 25200420 : return ((guint64)m[0] << 0) | ((guint64)m[1] << 8) |
1614 : 25200420 : ((guint64)m[2] << 16) | ((guint64)m[3] << 24) |
1615 : 25200420 : ((guint64)m[4] << 32) | ((guint64)m[5] << 40) |
1616 : 25200420 : ((guint64)m[6] << 48) | ((guint64)m[7] << 56);
1617 : : #else
1618 : : _attribute_aligned(4) const guint8 *m = ((const guint8 *)memory) + offset;
1619 : :
1620 : : return ((guint)m[0] << 0) | ((guint)m[1] << 8) |
1621 : : ((guint)m[2] << 16) | ((guint)m[3] << 24);
1622 : : #endif
1623 : : }
1624 : :
1625 : : /* The following constants are truncated on 32-bit machines */
1626 : : #define UTF8_ASCII_MASK ((gsize)0x8080808080808080L)
1627 : : #define UTF8_ASCII_SUB ((gsize)0x0101010101010101L)
1628 : :
1629 : : static inline int
1630 : 25200420 : utf8_word_is_ascii (gsize word)
1631 : : {
1632 : : /* True unless any byte is NULL or has the MSB set. */
1633 : 25200420 : return ((((word - UTF8_ASCII_SUB) | word) & UTF8_ASCII_MASK) == 0);
1634 : : }
1635 : :
1636 : : static void
1637 : 2064209 : utf8_verify_ascii (const char **strp,
1638 : : gsize *lenp)
1639 : : {
1640 : 2064209 : const char *str = *strp;
1641 : 2064209 : gsize len = lenp ? *lenp : strlen (str);
1642 : :
1643 : 8224360 : while (len > 0 && load_u8 (str, 0) < 128)
1644 : : {
1645 : 6933973 : if ((gpointer) align_to ((guintptr) str, sizeof (gsize)) == str)
1646 : : {
1647 : 14281001 : while (len >= 2 * sizeof (gsize))
1648 : : {
1649 : 25200420 : if (!utf8_word_is_ascii (load_word (str, 0)) ||
1650 : 12597364 : !utf8_word_is_ascii (load_word (str, sizeof (gsize))))
1651 : : break;
1652 : :
1653 : 12559090 : str += 2 * sizeof(gsize);
1654 : 12559090 : len -= 2 * sizeof(gsize);
1655 : : }
1656 : :
1657 : 11036242 : while (len > 0 && load_u8 (str, 0) < 128)
1658 : : {
1659 : 9913359 : if G_UNLIKELY (load_u8 (str, 0) == 0x00)
1660 : 599028 : goto out;
1661 : :
1662 : 9314331 : ++str;
1663 : 9314331 : --len;
1664 : : }
1665 : : }
1666 : : else
1667 : : {
1668 : 5212062 : if G_UNLIKELY (load_u8 (str, 0) == 0x00)
1669 : 174794 : goto out;
1670 : :
1671 : 5037268 : ++str;
1672 : 5037268 : --len;
1673 : : }
1674 : : }
1675 : :
1676 : 1290387 : out:
1677 : 2064209 : *strp = str;
1678 : :
1679 : 2064209 : if (lenp)
1680 : 2062077 : *lenp = len;
1681 : 2064209 : }
1682 : :
1683 : : #define UTF8_CHAR_IS_TAIL(_x) (((_x) & 0xC0) == 0x80)
1684 : :
1685 : : static void
1686 : 2064265 : utf8_verify (const char **strp,
1687 : : gsize *lenp)
1688 : : {
1689 : 2064265 : const char *str = *strp;
1690 : 2064265 : gsize len = lenp ? *lenp : strlen (str);
1691 : :
1692 : : /* See Unicode 10.0.0, Chapter 3, Section D92 */
1693 : :
1694 : 4146216 : while (len > 0)
1695 : : {
1696 : 2857774 : guint8 b = load_u8 (str, 0);
1697 : :
1698 : 2857774 : if (b == 0x00)
1699 : 775049 : goto out;
1700 : :
1701 : 2082725 : else if (b <= 0x7F)
1702 : : {
1703 : : /*
1704 : : * Special-case and optimize the ASCII case.
1705 : : */
1706 : 2062077 : utf8_verify_ascii ((const char **)&str, &len);
1707 : : }
1708 : :
1709 : 20648 : else if (b >= 0xC2 && b <= 0xDF)
1710 : : {
1711 : 3525 : if G_UNLIKELY (len < 2)
1712 : 10 : goto out;
1713 : 3515 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
1714 : 98 : goto out;
1715 : :
1716 : 3417 : str += 2;
1717 : 3417 : len -= 2;
1718 : :
1719 : : }
1720 : :
1721 : 17123 : else if (b == 0xE0)
1722 : : {
1723 : 1055 : if G_UNLIKELY (len < 3)
1724 : 5 : goto out;
1725 : 1050 : if G_UNLIKELY (load_u8 (str, 1) < 0xA0 || load_u8 (str, 1) > 0xBF)
1726 : 15 : goto out;
1727 : 1035 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1728 : 18 : goto out;
1729 : :
1730 : 1017 : str += 3;
1731 : 1017 : len -= 3;
1732 : : }
1733 : :
1734 : 16068 : else if (b >= 0xE1 && b <= 0xEC)
1735 : : {
1736 : 15196 : if G_UNLIKELY (len < 3)
1737 : 32 : goto out;
1738 : 15164 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
1739 : 37 : goto out;
1740 : 15127 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1741 : 2 : goto out;
1742 : :
1743 : 15125 : str += 3;
1744 : 15125 : len -= 3;
1745 : : }
1746 : :
1747 : 872 : else if (b == 0xED)
1748 : : {
1749 : 129 : if G_UNLIKELY (len < 3)
1750 : 2 : goto out;
1751 : 127 : if G_UNLIKELY (load_u8 (str, 1) < 0x80 || load_u8 (str, 1) > 0x9F)
1752 : 48 : goto out;
1753 : 79 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1754 : 2 : goto out;
1755 : :
1756 : 77 : str += 3;
1757 : 77 : len -= 3;
1758 : : }
1759 : :
1760 : 743 : else if (b >= 0xEE && b <= 0xEF)
1761 : : {
1762 : 227 : if G_UNLIKELY (len < 3)
1763 : 7 : goto out;
1764 : 220 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
1765 : 6 : goto out;
1766 : 214 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1767 : 0 : goto out;
1768 : :
1769 : 214 : str += 3;
1770 : 214 : len -= 3;
1771 : : }
1772 : :
1773 : 516 : else if (b == 0xF0)
1774 : : {
1775 : 41 : if G_UNLIKELY (len < 4)
1776 : 5 : goto out;
1777 : 36 : if G_UNLIKELY (load_u8 (str, 1) < 0x90 || load_u8 (str, 1) > 0xBF)
1778 : 15 : goto out;
1779 : 21 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1780 : 2 : goto out;
1781 : 19 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 3)))
1782 : 2 : goto out;
1783 : :
1784 : 17 : str += 4;
1785 : 17 : len -= 4;
1786 : : }
1787 : :
1788 : 475 : else if (b >= 0xF1 && b <= 0xF3)
1789 : : {
1790 : 18 : if G_UNLIKELY (len < 4)
1791 : 6 : goto out;
1792 : 12 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 1)))
1793 : 6 : goto out;
1794 : 6 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1795 : 2 : goto out;
1796 : 4 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 3)))
1797 : 2 : goto out;
1798 : :
1799 : 2 : str += 4;
1800 : 2 : len -= 4;
1801 : : }
1802 : :
1803 : 457 : else if (b == 0xF4)
1804 : : {
1805 : 18 : if G_UNLIKELY (len < 4)
1806 : 2 : goto out;
1807 : 16 : if G_UNLIKELY (load_u8 (str, 1) < 0x80 || load_u8 (str, 1) > 0x8F)
1808 : 7 : goto out;
1809 : 9 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 2)))
1810 : 2 : goto out;
1811 : 7 : if G_UNLIKELY (!UTF8_CHAR_IS_TAIL (load_u8 (str, 3)))
1812 : 2 : goto out;
1813 : :
1814 : 5 : str += 4;
1815 : 5 : len -= 4;
1816 : : }
1817 : :
1818 : 439 : else goto out;
1819 : : }
1820 : :
1821 : 1288442 : out:
1822 : 2064265 : *strp = str;
1823 : :
1824 : 2064265 : if (lenp)
1825 : 2064265 : *lenp = len;
1826 : 2064265 : }
1827 : :
1828 : : /**
1829 : : * g_utf8_validate:
1830 : : * @str: (array length=max_len) (element-type guint8): a pointer to character data
1831 : : * @max_len: max bytes to validate, or `-1` to go until nul
1832 : : * @end: (out) (optional) (transfer none): return location for end of valid data
1833 : : *
1834 : : * Validates UTF-8 encoded text.
1835 : : *
1836 : : * @str is the text to validate; if @str is nul-terminated, then @max_len can be
1837 : : * `-1`, otherwise @max_len should be the number of bytes to validate.
1838 : : *
1839 : : * If @end is non-`NULL`, then the end of the valid range will be stored there.
1840 : : * This is the first byte of the first invalid character if some bytes were
1841 : : * invalid, or the end of the text being validated otherwise — either the
1842 : : * trailing nul byte, or the first byte beyond @max_len (if it’s positive).
1843 : : *
1844 : : * Note that `g_utf8_validate()` returns `FALSE` if @max_len is positive and
1845 : : * any of the @max_len bytes are nul.
1846 : : *
1847 : : * Returns `TRUE` if all of @str was valid. Many GLib and GTK
1848 : : * routines require valid UTF-8 as input; so data read from a file
1849 : : * or the network should be checked with `g_utf8_validate()` before
1850 : : * doing anything else with it.
1851 : : *
1852 : : * Returns: `TRUE` if the text was valid UTF-8
1853 : : */
1854 : : gboolean
1855 : 1285728 : g_utf8_validate (const char *str,
1856 : : gssize max_len,
1857 : : const gchar **end)
1858 : : {
1859 : 1285728 : size_t max_len_unsigned = (max_len >= 0) ? (size_t) max_len : strlen (str);
1860 : :
1861 : 1285728 : return g_utf8_validate_len (str, max_len_unsigned, end);
1862 : : }
1863 : :
1864 : : /**
1865 : : * g_utf8_validate_len:
1866 : : * @str: (array length=max_len) (element-type guint8): a pointer to character data
1867 : : * @max_len: max bytes to validate
1868 : : * @end: (out) (optional) (transfer none): return location for end of valid data
1869 : : *
1870 : : * Validates UTF-8 encoded text.
1871 : : *
1872 : : * As with [func@GLib.utf8_validate], but @max_len must be set, and hence this
1873 : : * function will always return `FALSE` if any of the bytes of @str are nul.
1874 : : *
1875 : : * Returns: `TRUE` if the text was valid UTF-8
1876 : : * Since: 2.60
1877 : : */
1878 : : gboolean
1879 : 2064265 : g_utf8_validate_len (const char *str,
1880 : : gsize max_len,
1881 : : const gchar **end)
1882 : :
1883 : : {
1884 : 2064265 : utf8_verify (&str, &max_len);
1885 : :
1886 : 2064265 : if (end != NULL)
1887 : 2015144 : *end = str;
1888 : :
1889 : 2064265 : return max_len == 0;
1890 : : }
1891 : :
1892 : : /**
1893 : : * g_str_is_ascii:
1894 : : * @str: a string
1895 : : *
1896 : : * Determines if a string is pure ASCII. A string is pure ASCII if it
1897 : : * contains no bytes with the high bit set.
1898 : : *
1899 : : * Returns: true if @str is ASCII
1900 : : *
1901 : : * Since: 2.40
1902 : : */
1903 : : gboolean
1904 : 2132 : g_str_is_ascii (const gchar *str)
1905 : : {
1906 : 2132 : utf8_verify_ascii (&str, NULL);
1907 : :
1908 : 2132 : return *str == 0;
1909 : : }
1910 : :
1911 : : /**
1912 : : * g_unichar_validate:
1913 : : * @ch: a Unicode character
1914 : : *
1915 : : * Checks whether @ch is a valid Unicode character.
1916 : : *
1917 : : * Some possible integer values of @ch will not be valid. U+0000 is considered a
1918 : : * valid character, though it’s normally a string terminator.
1919 : : *
1920 : : * Returns: `TRUE` if @ch is a valid Unicode character
1921 : : **/
1922 : : gboolean
1923 : 10 : g_unichar_validate (gunichar ch)
1924 : : {
1925 : 10 : return UNICODE_VALID (ch);
1926 : : }
1927 : :
1928 : : /**
1929 : : * g_utf8_strreverse:
1930 : : * @str: a UTF-8 encoded string
1931 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
1932 : : * then the string is nul-terminated.
1933 : : *
1934 : : * Reverses a UTF-8 string.
1935 : : *
1936 : : * @str must be valid UTF-8 encoded text. (Use [func@GLib.utf8_validate] on all
1937 : : * text before trying to use UTF-8 utility functions with it.)
1938 : : *
1939 : : * This function is intended for programmatic uses of reversed strings.
1940 : : * It pays no attention to decomposed characters, combining marks, byte
1941 : : * order marks, directional indicators (LRM, LRO, etc) and similar
1942 : : * characters which might need special handling when reversing a string
1943 : : * for display purposes.
1944 : : *
1945 : : * Note that unlike [func@GLib.strreverse], this function returns
1946 : : * newly-allocated memory, which should be freed with [func@GLib.free] when
1947 : : * no longer needed.
1948 : : *
1949 : : * Returns: (transfer full): a newly-allocated string which is the reverse of @str
1950 : : *
1951 : : * Since: 2.2
1952 : : */
1953 : : gchar *
1954 : 232 : g_utf8_strreverse (const gchar *str,
1955 : : gssize len)
1956 : : {
1957 : : gchar *r, *result;
1958 : : const gchar *p;
1959 : :
1960 : 232 : if (len < 0)
1961 : 53 : len = strlen (str);
1962 : :
1963 : 232 : result = g_new (gchar, len + 1);
1964 : 232 : r = result + len;
1965 : 232 : p = str;
1966 : 4177 : while (r > result)
1967 : : {
1968 : 3945 : gchar *m, skip = g_utf8_skip[*(guchar*) p];
1969 : 3945 : r -= skip;
1970 : 3945 : g_assert (r >= result);
1971 : 7920 : for (m = r; skip; skip--)
1972 : 3975 : *m++ = *p++;
1973 : : }
1974 : 232 : result[len] = 0;
1975 : :
1976 : 232 : return result;
1977 : : }
1978 : :
1979 : : /**
1980 : : * g_utf8_make_valid:
1981 : : * @str: string to coerce into UTF-8
1982 : : * @len: the maximum length of @str to use, in bytes. If @len is negative,
1983 : : * then the string is nul-terminated.
1984 : : *
1985 : : * If the provided string is valid UTF-8, return a copy of it. If not,
1986 : : * return a copy in which bytes that could not be interpreted as valid Unicode
1987 : : * are replaced with the Unicode replacement character (U+FFFD).
1988 : : *
1989 : : * For example, this is an appropriate function to use if you have received
1990 : : * a string that was incorrectly declared to be UTF-8, and you need a valid
1991 : : * UTF-8 version of it that can be logged or displayed to the user, with the
1992 : : * assumption that it is close enough to ASCII or UTF-8 to be mostly
1993 : : * readable as-is.
1994 : : *
1995 : : * Returns: (transfer full): a valid UTF-8 string whose content resembles @str
1996 : : *
1997 : : * Since: 2.52
1998 : : */
1999 : : gchar *
2000 : 309 : g_utf8_make_valid (const gchar *str,
2001 : : gssize len)
2002 : : {
2003 : : GString *string;
2004 : : const gchar *remainder, *invalid;
2005 : : gsize remaining_bytes, valid_bytes;
2006 : :
2007 : 309 : g_return_val_if_fail (str != NULL, NULL);
2008 : :
2009 : 309 : if (len < 0)
2010 : 298 : len = strlen (str);
2011 : :
2012 : 309 : string = NULL;
2013 : 309 : remainder = str;
2014 : 309 : remaining_bytes = len;
2015 : :
2016 : 378 : while (remaining_bytes != 0)
2017 : : {
2018 : 357 : if (g_utf8_validate (remainder, remaining_bytes, &invalid))
2019 : 288 : break;
2020 : 69 : valid_bytes = invalid - remainder;
2021 : :
2022 : 69 : if (string == NULL)
2023 : 39 : string = g_string_sized_new (remaining_bytes);
2024 : :
2025 : 69 : g_string_append_len (string, remainder, valid_bytes);
2026 : : /* append U+FFFD REPLACEMENT CHARACTER */
2027 : 69 : g_string_append (string, "\357\277\275");
2028 : :
2029 : 69 : remaining_bytes -= valid_bytes + 1;
2030 : 69 : remainder = invalid + 1;
2031 : : }
2032 : :
2033 : 309 : if (string == NULL)
2034 : 270 : return g_strndup (str, len);
2035 : :
2036 : 39 : g_string_append_len (string, remainder, remaining_bytes);
2037 : : g_string_append_c (string, '\0');
2038 : :
2039 : 39 : g_assert (g_utf8_validate (string->str, -1, NULL));
2040 : :
2041 : 39 : return g_string_free (string, FALSE);
2042 : : }
|