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 Red Hat, Inc.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General Public
19 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include <stdlib.h>
25 : : #ifdef HAVE_CODESET
26 : : #include <langinfo.h>
27 : : #endif
28 : : #include <string.h>
29 : :
30 : : #ifdef G_PLATFORM_WIN32
31 : : #include <stdio.h>
32 : : #define STRICT
33 : : #include <windows.h>
34 : : #undef STRICT
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 : :
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 or %NULL.
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 : 638555 : g_utf8_find_next_char (const gchar *p,
178 : : const gchar *end)
179 : : {
180 [ + + ]: 638555 : if (end)
181 : : {
182 [ + + + + ]: 568157 : for (++p; p < end && (*p & 0xc0) == 0x80; ++p)
183 : : ;
184 [ + + ]: 568149 : return (p >= end) ? NULL : (gchar *)p;
185 : : }
186 : : else
187 : : {
188 [ + + ]: 70420 : for (++p; (*p & 0xc0) == 0x80; ++p)
189 : : ;
190 : 70406 : 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 g_utf8_find_prev_char() instead.
204 : : *
205 : : * Returns: (transfer none) (not nullable): a pointer to the found character
206 : : */
207 : : gchar *
208 : 282 : g_utf8_prev_char (const gchar *p)
209 : : {
210 : : while (TRUE)
211 : : {
212 : 465 : p--;
213 [ + + ]: 465 : if ((*p & 0xc0) != 0x80)
214 : 282 : return (gchar *)p;
215 : : }
216 : : }
217 : :
218 : : /**
219 : : * g_utf8_strlen:
220 : : * @p: pointer to the start of a UTF-8 encoded string
221 : : * @max: the maximum number of bytes to examine. If @max
222 : : * is less than 0, then the string is assumed to be
223 : : * nul-terminated. If @max is 0, @p will not be examined and
224 : : * may be %NULL. If @max is greater than 0, up to @max
225 : : * bytes are examined
226 : : *
227 : : * Computes the length of the string in characters, not including
228 : : * the terminating nul character. If the @max'th byte falls in the
229 : : * middle of a character, the last (partial) character is not counted.
230 : : *
231 : : * Returns: the length of the string in characters
232 : : */
233 : : glong
234 : 51936 : g_utf8_strlen (const gchar *p,
235 : : gssize max)
236 : : {
237 : 51936 : glong len = 0;
238 : 51936 : const gchar *start = p;
239 : 51936 : g_return_val_if_fail (p != NULL || max == 0, 0);
240 : :
241 [ + + ]: 51936 : if (max < 0)
242 : : {
243 [ + + ]: 142402 : while (*p)
244 : : {
245 : 90491 : p = g_utf8_next_char (p);
246 : 90491 : ++len;
247 : : }
248 : : }
249 : : else
250 : : {
251 [ + + - + ]: 25 : if (max == 0 || !*p)
252 : 3 : return 0;
253 : :
254 : 22 : p = g_utf8_next_char (p);
255 : :
256 [ + + + + ]: 1579 : while (p - start < max && *p)
257 : : {
258 : 1557 : ++len;
259 : 1557 : p = g_utf8_next_char (p);
260 : : }
261 : :
262 : : /* only do the last len increment if we got a complete
263 : : * char (don't count partial chars)
264 : : */
265 [ + + ]: 22 : if (p - start <= max)
266 : 16 : ++len;
267 : : }
268 : :
269 : 51933 : return len;
270 : : }
271 : :
272 : : /**
273 : : * g_utf8_substring:
274 : : * @str: a UTF-8 encoded string
275 : : * @start_pos: a character offset within @str
276 : : * @end_pos: another character offset within @str,
277 : : * or `-1` to indicate the end of the string
278 : : *
279 : : * Copies a substring out of a UTF-8 encoded string.
280 : : * The substring will contain @end_pos - @start_pos characters.
281 : : *
282 : : * Since GLib 2.72, `-1` can be passed to @end_pos to indicate the
283 : : * end of the string.
284 : : *
285 : : * Returns: (transfer full): a newly allocated copy of the requested
286 : : * substring. Free with g_free() when no longer needed.
287 : : *
288 : : * Since: 2.30
289 : : */
290 : : gchar *
291 : 5 : g_utf8_substring (const gchar *str,
292 : : glong start_pos,
293 : : glong end_pos)
294 : : {
295 : : gchar *start, *end, *out;
296 : :
297 : 5 : g_return_val_if_fail (end_pos >= start_pos || end_pos == -1, NULL);
298 : :
299 : 5 : start = g_utf8_offset_to_pointer (str, start_pos);
300 : :
301 [ + + ]: 5 : if (end_pos == -1)
302 : : {
303 : 1 : glong length = g_utf8_strlen (start, -1);
304 : 1 : end = g_utf8_offset_to_pointer (start, length);
305 : : }
306 : : else
307 : : {
308 : 4 : end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
309 : : }
310 : :
311 : 5 : out = g_malloc (end - start + 1);
312 : 5 : memcpy (out, start, end - start);
313 : 5 : out[end - start] = 0;
314 : :
315 : 5 : return out;
316 : : }
317 : :
318 : : /**
319 : : * g_utf8_get_char:
320 : : * @p: a pointer to Unicode character encoded as UTF-8
321 : : *
322 : : * Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
323 : : *
324 : : * If @p does not point to a valid UTF-8 encoded character, results
325 : : * are undefined. If you are not sure that the bytes are complete
326 : : * valid Unicode characters, you should use g_utf8_get_char_validated()
327 : : * instead.
328 : : *
329 : : * Returns: the resulting character
330 : : */
331 : : gunichar
332 : 9671717 : g_utf8_get_char (const gchar *p)
333 : : {
334 : 9671717 : int i, mask = 0, len;
335 : : gunichar result;
336 : 9671717 : unsigned char c = (unsigned char) *p;
337 : :
338 [ + + + + : 9671717 : UTF8_COMPUTE (c, mask, len);
+ + + + +
+ + + ]
339 [ + + ]: 9671717 : if (len == -1)
340 : 10 : return (gunichar)-1;
341 [ + + + + ]: 12307094 : UTF8_GET (result, p, i, mask, len);
342 : :
343 : 9671707 : return result;
344 : : }
345 : :
346 : : /**
347 : : * g_utf8_offset_to_pointer:
348 : : * @str: a UTF-8 encoded string
349 : : * @offset: a character offset within @str
350 : : *
351 : : * Converts from an integer character offset to a pointer to a position
352 : : * within the string.
353 : : *
354 : : * Since 2.10, this function allows to pass a negative @offset to
355 : : * step backwards. It is usually worth stepping backwards from the end
356 : : * instead of forwards if @offset is in the last fourth of the string,
357 : : * since moving forward is about 3 times faster than moving backward.
358 : : *
359 : : * Note that this function doesn't abort when reaching the end of @str.
360 : : * Therefore you should be sure that @offset is within string boundaries
361 : : * before calling that function. Call g_utf8_strlen() when unsure.
362 : : * This limitation exists as this function is called frequently during
363 : : * text rendering and therefore has to be as fast as possible.
364 : : *
365 : : * Returns: (transfer none): the resulting pointer
366 : : */
367 : : gchar *
368 : 580726 : g_utf8_offset_to_pointer (const gchar *str,
369 : : glong offset)
370 : : {
371 : 580726 : const gchar *s = str;
372 : :
373 [ + + ]: 580726 : if (offset > 0)
374 [ + + ]: 74032020 : while (offset--)
375 : 73742005 : s = g_utf8_next_char (s);
376 : : else
377 : : {
378 : : const char *s1;
379 : :
380 : : /* This nice technique for fast backwards stepping
381 : : * through a UTF-8 string was dubbed "stutter stepping"
382 : : * by its inventor, Larry Ewing.
383 : : */
384 [ + + ]: 954612 : while (offset)
385 : : {
386 : 663901 : s1 = s;
387 : 663901 : s += offset;
388 [ + + ]: 764185 : while ((*s & 0xc0) == 0x80)
389 : 100284 : s--;
390 : :
391 : 663901 : offset += g_utf8_pointer_to_offset (s, s1);
392 : : }
393 : : }
394 : :
395 : 580726 : return (gchar *)s;
396 : : }
397 : :
398 : : /**
399 : : * g_utf8_pointer_to_offset:
400 : : * @str: a UTF-8 encoded string
401 : : * @pos: a pointer to a position within @str
402 : : *
403 : : * Converts from a pointer to position within a string to an integer
404 : : * character offset.
405 : : *
406 : : * Since 2.10, this function allows @pos to be before @str, and returns
407 : : * a negative offset in this case.
408 : : *
409 : : * Returns: the resulting character offset
410 : : */
411 : : glong
412 : 1534544 : g_utf8_pointer_to_offset (const gchar *str,
413 : : const gchar *pos)
414 : : {
415 : 1534544 : const gchar *s = str;
416 : 1534544 : glong offset = 0;
417 : :
418 [ + + ]: 1534544 : if (pos < str)
419 : 289941 : offset = - g_utf8_pointer_to_offset (pos, str);
420 : : else
421 [ + + ]: 222470149 : while (s < pos)
422 : : {
423 : 221225546 : s = g_utf8_next_char (s);
424 : 221225546 : offset++;
425 : : }
426 : :
427 : 1534544 : return offset;
428 : : }
429 : :
430 : :
431 : : /**
432 : : * g_utf8_strncpy:
433 : : * @dest: (transfer none): buffer to fill with characters from @src
434 : : * @src: UTF-8 encoded string
435 : : * @n: character count
436 : : *
437 : : * Like the standard C strncpy() function, but copies a given number
438 : : * of characters instead of a given number of bytes. The @src string
439 : : * must be valid UTF-8 encoded text. (Use g_utf8_validate() on all
440 : : * text before trying to use UTF-8 utility functions with it.)
441 : : *
442 : : * Note you must ensure @dest is at least 4 * @n + 1 to fit the
443 : : * largest possible UTF-8 characters
444 : : *
445 : : * Returns: (transfer none): @dest
446 : : */
447 : : gchar *
448 : 8 : g_utf8_strncpy (gchar *dest,
449 : : const gchar *src,
450 : : gsize n)
451 : : {
452 : 8 : const gchar *s = src;
453 [ + + + + ]: 35 : while (n && *s)
454 : : {
455 : 27 : s = g_utf8_next_char(s);
456 : 27 : n--;
457 : : }
458 : 8 : strncpy(dest, src, s - src);
459 : 8 : dest[s - src] = 0;
460 : 8 : return dest;
461 : : }
462 : :
463 : : /**
464 : : * g_utf8_truncate_middle:
465 : : * @string: (transfer none): a nul-terminated UTF-8 encoded string
466 : : * @truncate_length: the new size of @string, in characters, including the ellipsis character
467 : : *
468 : : * Cuts off the middle of the string, preserving half of @truncate_length
469 : : * characters at the beginning and half at the end.
470 : : *
471 : : * If @string is already short enough, this returns a copy of @string.
472 : : * If @truncate_length is `0`, an empty string is returned.
473 : : *
474 : : * Returns: (transfer full): a newly-allocated copy of @string ellipsized in the middle
475 : : *
476 : : * Since: 2.78
477 : : */
478 : : gchar *
479 : 34 : g_utf8_truncate_middle (const gchar *string,
480 : : gsize truncate_length)
481 : : {
482 : 34 : const gchar *ellipsis = "…";
483 : 34 : const gsize ellipsis_bytes = strlen (ellipsis);
484 : :
485 : : gsize length;
486 : : gsize left_substring_length;
487 : : gchar *left_substring_end;
488 : : gchar *right_substring_begin;
489 : : gchar *right_substring_end;
490 : : gsize left_bytes;
491 : : gsize right_bytes;
492 : : gchar *result;
493 : :
494 : 34 : g_return_val_if_fail (string != NULL, NULL);
495 : :
496 : 34 : length = g_utf8_strlen (string, -1);
497 : : /* Current string already smaller than requested length */
498 [ + + ]: 34 : if (length <= truncate_length)
499 : 8 : return g_strdup (string);
500 [ + + ]: 26 : if (truncate_length == 0)
501 : 2 : return g_strdup ("");
502 : :
503 : : /* Find substrings to keep, ignore ellipsis character for that */
504 : 24 : truncate_length -= 1;
505 : :
506 : 24 : left_substring_length = truncate_length / 2;
507 : :
508 : 24 : left_substring_end = g_utf8_offset_to_pointer (string, left_substring_length);
509 : 24 : right_substring_begin = g_utf8_offset_to_pointer (left_substring_end,
510 : 24 : length - truncate_length);
511 : 24 : right_substring_end = g_utf8_offset_to_pointer (right_substring_begin,
512 : 24 : truncate_length - left_substring_length);
513 : :
514 : 24 : g_assert (*right_substring_end == '\0');
515 : :
516 : 24 : left_bytes = left_substring_end - string;
517 : 24 : right_bytes = right_substring_end - right_substring_begin;
518 : :
519 : 24 : result = g_malloc (left_bytes + ellipsis_bytes + right_bytes + 1);
520 : :
521 : 24 : strncpy (result, string, left_bytes);
522 : 24 : memcpy (result + left_bytes, ellipsis, ellipsis_bytes);
523 : 24 : strncpy (result + left_bytes + ellipsis_bytes, right_substring_begin, right_bytes);
524 : 24 : result[left_bytes + ellipsis_bytes + right_bytes] = '\0';
525 : :
526 : 24 : return result;
527 : : }
528 : :
529 : : /* unicode_strchr */
530 : :
531 : : /**
532 : : * g_unichar_to_utf8:
533 : : * @c: a Unicode character code
534 : : * @outbuf: (out caller-allocates) (optional): output buffer, must have at
535 : : * least 6 bytes of space. If %NULL, the length will be computed and
536 : : * returned and nothing will be written to @outbuf.
537 : : *
538 : : * Converts a single character to UTF-8.
539 : : *
540 : : * Returns: number of bytes written
541 : : */
542 : : int
543 : 911622 : g_unichar_to_utf8 (gunichar c,
544 : : gchar *outbuf)
545 : : {
546 : : /* If this gets modified, also update the copy in g_string_insert_unichar() */
547 : 911622 : guint len = 0;
548 : : int first;
549 : : int i;
550 : :
551 [ + + ]: 911622 : if (c < 0x80)
552 : : {
553 : 180045 : first = 0;
554 : 180045 : len = 1;
555 : : }
556 [ + + ]: 731577 : else if (c < 0x800)
557 : : {
558 : 154010 : first = 0xc0;
559 : 154010 : len = 2;
560 : : }
561 [ + + ]: 577567 : else if (c < 0x10000)
562 : : {
563 : 550992 : first = 0xe0;
564 : 550992 : len = 3;
565 : : }
566 [ + - ]: 26575 : else if (c < 0x200000)
567 : : {
568 : 26575 : first = 0xf0;
569 : 26575 : len = 4;
570 : : }
571 [ # # ]: 0 : else if (c < 0x4000000)
572 : : {
573 : 0 : first = 0xf8;
574 : 0 : len = 5;
575 : : }
576 : : else
577 : : {
578 : 0 : first = 0xfc;
579 : 0 : len = 6;
580 : : }
581 : :
582 [ + + ]: 911622 : if (outbuf)
583 : : {
584 [ + + ]: 2234741 : for (i = len - 1; i > 0; --i)
585 : : {
586 : 1327285 : outbuf[i] = (c & 0x3f) | 0x80;
587 : 1327285 : c >>= 6;
588 : : }
589 : 907456 : outbuf[0] = c | first;
590 : : }
591 : :
592 : 911622 : return len;
593 : : }
594 : :
595 : : /**
596 : : * g_utf8_strchr:
597 : : * @p: a nul-terminated UTF-8 encoded string
598 : : * @len: the maximum length of @p
599 : : * @c: a Unicode character
600 : : *
601 : : * Finds the leftmost occurrence of the given Unicode character
602 : : * in a UTF-8 encoded string, while limiting the search to @len bytes.
603 : : * If @len is -1, allow unbounded search.
604 : : *
605 : : * Returns: (transfer none) (nullable): %NULL if the string does not contain the character,
606 : : * otherwise, a pointer to the start of the leftmost occurrence
607 : : * of the character in the string.
608 : : */
609 : : gchar *
610 : 43796 : g_utf8_strchr (const char *p,
611 : : gssize len,
612 : : gunichar c)
613 : : {
614 : : gchar ch[10];
615 : :
616 : 43796 : gint charlen = g_unichar_to_utf8 (c, ch);
617 : 43796 : ch[charlen] = '\0';
618 : :
619 : 43796 : return g_strstr_len (p, len, ch);
620 : : }
621 : :
622 : :
623 : : /**
624 : : * g_utf8_strrchr:
625 : : * @p: a nul-terminated UTF-8 encoded string
626 : : * @len: the maximum length of @p
627 : : * @c: a Unicode character
628 : : *
629 : : * Find the rightmost occurrence of the given Unicode character
630 : : * in a UTF-8 encoded string, while limiting the search to @len bytes.
631 : : * If @len is -1, allow unbounded search.
632 : : *
633 : : * Returns: (transfer none) (nullable): %NULL if the string does not contain the character,
634 : : * otherwise, a pointer to the start of the rightmost occurrence
635 : : * of the character in the string.
636 : : */
637 : : gchar *
638 : 5 : g_utf8_strrchr (const char *p,
639 : : gssize len,
640 : : gunichar c)
641 : : {
642 : : gchar ch[10];
643 : :
644 : 5 : gint charlen = g_unichar_to_utf8 (c, ch);
645 : 5 : ch[charlen] = '\0';
646 : :
647 : 5 : return g_strrstr_len (p, len, ch);
648 : : }
649 : :
650 : :
651 : : /* Like g_utf8_get_char, but take a maximum length
652 : : * and return (gunichar)-2 on incomplete trailing character;
653 : : * also check for malformed or overlong sequences
654 : : * and return (gunichar)-1 in this case.
655 : : */
656 : : static inline gunichar
657 : 2367284 : g_utf8_get_char_extended (const gchar *p,
658 : : gssize max_len)
659 : : {
660 : : gsize i, len;
661 : : gunichar min_code;
662 : 2367284 : gunichar wc = (guchar) *p;
663 : 2367284 : const gunichar partial_sequence = (gunichar) -2;
664 : 2367284 : const gunichar malformed_sequence = (gunichar) -1;
665 : :
666 [ + + ]: 2367284 : if (wc < 0x80)
667 : : {
668 : 2157492 : return wc;
669 : : }
670 [ - + ]: 209792 : else if (G_UNLIKELY (wc < 0xc0))
671 : : {
672 : 0 : return malformed_sequence;
673 : : }
674 [ + + ]: 209792 : else if (wc < 0xe0)
675 : : {
676 : 34630 : len = 2;
677 : 34630 : wc &= 0x1f;
678 : 34630 : min_code = 1 << 7;
679 : : }
680 [ + + ]: 175162 : else if (wc < 0xf0)
681 : : {
682 : 166688 : len = 3;
683 : 166688 : wc &= 0x0f;
684 : 166688 : min_code = 1 << 11;
685 : : }
686 [ + + ]: 8474 : else if (wc < 0xf8)
687 : : {
688 : 8437 : len = 4;
689 : 8437 : wc &= 0x07;
690 : 8437 : min_code = 1 << 16;
691 : : }
692 [ + + ]: 37 : else if (wc < 0xfc)
693 : : {
694 : 6 : len = 5;
695 : 6 : wc &= 0x03;
696 : 6 : min_code = 1 << 21;
697 : : }
698 [ + - ]: 31 : else if (wc < 0xfe)
699 : : {
700 : 31 : len = 6;
701 : 31 : wc &= 0x01;
702 : 31 : min_code = 1 << 26;
703 : : }
704 : : else
705 : : {
706 : 0 : return malformed_sequence;
707 : : }
708 : :
709 [ + + + + ]: 209792 : if (G_UNLIKELY (max_len >= 0 && len > (gsize) max_len))
710 : : {
711 [ + + ]: 595 : for (i = 1; i < (gsize) max_len; i++)
712 : : {
713 [ + + ]: 185 : if ((((guchar *)p)[i] & 0xc0) != 0x80)
714 : 1 : return malformed_sequence;
715 : : }
716 : 410 : return partial_sequence;
717 : : }
718 : :
719 [ + + ]: 601961 : for (i = 1; i < len; ++i)
720 : : {
721 : 392614 : gunichar ch = ((guchar *)p)[i];
722 : :
723 [ + + ]: 392614 : if (G_UNLIKELY ((ch & 0xc0) != 0x80))
724 : : {
725 [ + + ]: 34 : if (ch)
726 : 7 : return malformed_sequence;
727 : : else
728 : 27 : return partial_sequence;
729 : : }
730 : :
731 : 392580 : wc <<= 6;
732 : 392580 : wc |= (ch & 0x3f);
733 : : }
734 : :
735 [ - + ]: 209347 : if (G_UNLIKELY (wc < min_code))
736 : 0 : return malformed_sequence;
737 : :
738 : 209347 : return wc;
739 : : }
740 : :
741 : : /**
742 : : * g_utf8_get_char_validated:
743 : : * @p: a pointer to Unicode character encoded as UTF-8
744 : : * @max_len: the maximum number of bytes to read, or -1 if @p is nul-terminated
745 : : *
746 : : * Convert a sequence of bytes encoded as UTF-8 to a Unicode character.
747 : : * This function checks for incomplete characters, for invalid characters
748 : : * such as characters that are out of the range of Unicode, and for
749 : : * overlong encodings of valid characters.
750 : : *
751 : : * Note that g_utf8_get_char_validated() returns (gunichar)-2 if
752 : : * @max_len is positive and any of the bytes in the first UTF-8 character
753 : : * sequence are nul.
754 : : *
755 : : * Returns: the resulting character. If @p points to a partial
756 : : * sequence at the end of a string that could begin a valid
757 : : * character (or if @max_len is zero), returns (gunichar)-2;
758 : : * otherwise, if @p does not point to a valid UTF-8 encoded
759 : : * Unicode character, returns (gunichar)-1.
760 : : */
761 : : gunichar
762 : 2362190 : g_utf8_get_char_validated (const gchar *p,
763 : : gssize max_len)
764 : : {
765 : : gunichar result;
766 : :
767 [ + + ]: 2362190 : if (max_len == 0)
768 : 1 : return (gunichar)-2;
769 : :
770 : 2362189 : result = g_utf8_get_char_extended (p, max_len);
771 : :
772 : : /* Disallow codepoint U+0000 as it’s a nul byte,
773 : : * and all string handling in GLib is nul-terminated */
774 [ + + + + ]: 2362189 : if (result == 0 && max_len > 0)
775 : 2 : return (gunichar) -2;
776 : :
777 [ + + ]: 2362187 : if (result & 0x80000000)
778 : 413 : return result;
779 [ + - - + ]: 2361774 : else if (!UNICODE_VALID (result))
780 : 0 : return (gunichar)-1;
781 : : else
782 : 2361774 : return result;
783 : : }
784 : :
785 : : #define CONT_BYTE_FAST(p) ((guchar)*p++ & 0x3f)
786 : :
787 : : /**
788 : : * g_utf8_to_ucs4_fast:
789 : : * @str: a UTF-8 encoded string
790 : : * @len: the maximum length of @str to use, in bytes. If @len < 0,
791 : : * then the string is nul-terminated.
792 : : * @items_written: (out) (optional): location to store the
793 : : * number of characters in the result, or %NULL.
794 : : *
795 : : * Convert a string from UTF-8 to a 32-bit fixed width
796 : : * representation as UCS-4, assuming valid UTF-8 input.
797 : : * This function is roughly twice as fast as g_utf8_to_ucs4()
798 : : * but does no error checking on the input. A trailing 0 character
799 : : * will be added to the string after the converted text.
800 : : *
801 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
802 : : * This value must be freed with g_free().
803 : : */
804 : : gunichar *
805 : 23 : g_utf8_to_ucs4_fast (const gchar *str,
806 : : glong len,
807 : : glong *items_written)
808 : : {
809 : : gunichar *result;
810 : : gint n_chars, i;
811 : : const gchar *p;
812 : :
813 : 23 : g_return_val_if_fail (str != NULL, NULL);
814 : :
815 : 23 : p = str;
816 : 23 : n_chars = 0;
817 [ + + ]: 23 : if (len < 0)
818 : : {
819 [ + + ]: 281 : while (*p)
820 : : {
821 : 264 : p = g_utf8_next_char (p);
822 : 264 : ++n_chars;
823 : : }
824 : : }
825 : : else
826 : : {
827 [ + + + - ]: 255 : while (p < str + len && *p)
828 : : {
829 : 249 : p = g_utf8_next_char (p);
830 : 249 : ++n_chars;
831 : : }
832 : : }
833 : :
834 : 23 : result = g_new (gunichar, n_chars + 1);
835 : :
836 : 23 : p = str;
837 [ + + ]: 536 : for (i=0; i < n_chars; i++)
838 : : {
839 : 513 : guchar first = (guchar)*p++;
840 : : gunichar wc;
841 : :
842 [ + + ]: 513 : if (first < 0xc0)
843 : : {
844 : : /* We really hope first < 0x80, but we don't want to test an
845 : : * extra branch for invalid input, which this function
846 : : * does not care about. Handling unexpected continuation bytes
847 : : * here will do the least damage. */
848 : 231 : wc = first;
849 : : }
850 : : else
851 : : {
852 : 282 : gunichar c1 = CONT_BYTE_FAST(p);
853 [ + + ]: 282 : if (first < 0xe0)
854 : : {
855 : 167 : wc = ((first & 0x1f) << 6) | c1;
856 : : }
857 : : else
858 : : {
859 : 115 : gunichar c2 = CONT_BYTE_FAST(p);
860 [ + + ]: 115 : if (first < 0xf0)
861 : : {
862 : 110 : wc = ((first & 0x0f) << 12) | (c1 << 6) | c2;
863 : : }
864 : : else
865 : : {
866 : 5 : gunichar c3 = CONT_BYTE_FAST(p);
867 : 5 : wc = ((first & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
868 [ - + ]: 5 : if (G_UNLIKELY (first >= 0xf8))
869 : : {
870 : : /* This can't be valid UTF-8, but g_utf8_next_char()
871 : : * and company allow out-of-range sequences */
872 : 0 : gunichar mask = 1 << 20;
873 [ # # ]: 0 : while ((wc & mask) != 0)
874 : : {
875 : 0 : wc <<= 6;
876 : 0 : wc |= CONT_BYTE_FAST(p);
877 : 0 : mask <<= 5;
878 : : }
879 : 0 : wc &= mask - 1;
880 : : }
881 : : }
882 : : }
883 : : }
884 : 513 : result[i] = wc;
885 : : }
886 : 23 : result[i] = 0;
887 : :
888 [ + + ]: 23 : if (items_written)
889 : 15 : *items_written = i;
890 : :
891 : 23 : return result;
892 : : }
893 : :
894 : : static gpointer
895 : 386404 : try_malloc_n (gsize n_blocks, gsize n_block_bytes, GError **error)
896 : : {
897 : 386404 : gpointer ptr = g_try_malloc_n (n_blocks, n_block_bytes);
898 [ - + ]: 386404 : if (ptr == NULL)
899 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_MEMORY,
900 : : _("Failed to allocate memory"));
901 : 386404 : return ptr;
902 : : }
903 : :
904 : : /**
905 : : * g_utf8_to_ucs4:
906 : : * @str: a UTF-8 encoded string
907 : : * @len: the maximum length of @str to use, in bytes. If @len < 0,
908 : : * then the string is nul-terminated.
909 : : * @items_read: (out) (optional): location to store number of
910 : : * bytes read, or %NULL.
911 : : * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
912 : : * returned in case @str contains a trailing partial
913 : : * character. If an error occurs then the index of the
914 : : * invalid input is stored here.
915 : : * @items_written: (out) (optional): location to store number
916 : : * of characters written or %NULL. The value here stored does not include
917 : : * the trailing 0 character.
918 : : * @error: location to store the error occurring, or %NULL to ignore
919 : : * errors. Any of the errors in #GConvertError other than
920 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
921 : : *
922 : : * Convert a string from UTF-8 to a 32-bit fixed width
923 : : * representation as UCS-4. A trailing 0 character will be added to the
924 : : * string after the converted text.
925 : : *
926 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
927 : : * This value must be freed with g_free(). If an error occurs,
928 : : * %NULL will be returned and @error set.
929 : : */
930 : : gunichar *
931 : 111 : g_utf8_to_ucs4 (const gchar *str,
932 : : glong len,
933 : : glong *items_read,
934 : : glong *items_written,
935 : : GError **error)
936 : : {
937 : 111 : gunichar *result = NULL;
938 : : gint n_chars, i;
939 : : const gchar *in;
940 : :
941 : 111 : in = str;
942 : 111 : n_chars = 0;
943 [ + + + + : 855 : while ((len < 0 || str + len - in > 0) && *in)
+ + ]
944 : : {
945 [ + + ]: 770 : gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
946 [ + + ]: 770 : if (wc & 0x80000000)
947 : : {
948 [ + + ]: 26 : if (wc == (gunichar)-2)
949 : : {
950 [ + + ]: 23 : if (items_read)
951 : 12 : break;
952 : : else
953 : 11 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
954 : : _("Partial character sequence at end of input"));
955 : : }
956 : : else
957 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
958 : : _("Invalid byte sequence in conversion input"));
959 : :
960 : 14 : goto err_out;
961 : : }
962 : :
963 : 744 : n_chars++;
964 : :
965 : 744 : in = g_utf8_next_char (in);
966 : : }
967 : :
968 : 97 : result = try_malloc_n (n_chars + 1, sizeof (gunichar), error);
969 [ - + ]: 97 : if (result == NULL)
970 : 0 : goto err_out;
971 : :
972 : 97 : in = str;
973 [ + + ]: 829 : for (i=0; i < n_chars; i++)
974 : : {
975 : 732 : result[i] = g_utf8_get_char (in);
976 : 732 : in = g_utf8_next_char (in);
977 : : }
978 : 97 : result[i] = 0;
979 : :
980 [ + + ]: 97 : if (items_written)
981 : 77 : *items_written = n_chars;
982 : :
983 : 20 : err_out:
984 [ + + ]: 111 : if (items_read)
985 : 52 : *items_read = in - str;
986 : :
987 : 111 : return result;
988 : : }
989 : :
990 : : /**
991 : : * g_ucs4_to_utf8:
992 : : * @str: (array length=len) (element-type gunichar): a UCS-4 encoded string
993 : : * @len: the maximum length (number of characters) of @str to use.
994 : : * If @len < 0, then the string is nul-terminated.
995 : : * @items_read: (out) (optional): location to store number of
996 : : * characters read, or %NULL.
997 : : * @items_written: (out) (optional): location to store number
998 : : * of bytes written or %NULL. The value here stored does not include the
999 : : * trailing 0 byte.
1000 : : * @error: location to store the error occurring, or %NULL to ignore
1001 : : * errors. Any of the errors in #GConvertError other than
1002 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1003 : : *
1004 : : * Convert a string from a 32-bit fixed width representation as UCS-4.
1005 : : * to UTF-8. The result will be terminated with a 0 byte.
1006 : : *
1007 : : * Returns: (transfer full): a pointer to a newly allocated UTF-8 string.
1008 : : * This value must be freed with g_free(). If an error occurs,
1009 : : * %NULL will be returned and @error set. In that case, @items_read
1010 : : * will be set to the position of the first invalid input character.
1011 : : */
1012 : : gchar *
1013 : 386034 : g_ucs4_to_utf8 (const gunichar *str,
1014 : : glong len,
1015 : : glong *items_read,
1016 : : glong *items_written,
1017 : : GError **error)
1018 : : {
1019 : : gint result_length;
1020 : 386034 : gchar *result = NULL;
1021 : : gchar *p;
1022 : : gint i;
1023 : :
1024 : 386034 : result_length = 0;
1025 [ + + + + ]: 1243508 : for (i = 0; len < 0 || i < len ; i++)
1026 : : {
1027 [ + + ]: 1243504 : if (!str[i])
1028 : 386027 : break;
1029 : :
1030 [ + + ]: 857477 : if (str[i] >= 0x80000000)
1031 : : {
1032 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1033 : : _("Character out of range for UTF-8"));
1034 : 3 : goto err_out;
1035 : : }
1036 : :
1037 [ + + + + : 857474 : result_length += UTF8_LENGTH (str[i]);
+ + - + -
- ]
1038 : : }
1039 : :
1040 : 386031 : result = try_malloc_n (result_length + 1, 1, error);
1041 [ - + ]: 386031 : if (result == NULL)
1042 : 0 : goto err_out;
1043 : :
1044 : 386031 : p = result;
1045 : :
1046 : 386031 : i = 0;
1047 [ + + ]: 1243499 : while (p < result + result_length)
1048 : 857468 : p += g_unichar_to_utf8 (str[i++], p);
1049 : :
1050 : 386031 : *p = '\0';
1051 : :
1052 [ + + ]: 386031 : if (items_written)
1053 : 17 : *items_written = p - result;
1054 : :
1055 : 386014 : err_out:
1056 [ + + ]: 386034 : if (items_read)
1057 : 19 : *items_read = i;
1058 : :
1059 : 386034 : return result;
1060 : : }
1061 : :
1062 : : #define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000)
1063 : :
1064 : : /**
1065 : : * g_utf16_to_utf8:
1066 : : * @str: (array length=len) (element-type guint16): a UTF-16 encoded string
1067 : : * @len: the maximum length (number of #gunichar2) of @str to use.
1068 : : * If @len < 0, then the string is nul-terminated.
1069 : : * @items_read: (out) (optional): location to store number of
1070 : : * words read, or %NULL. If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will
1071 : : * be returned in case @str contains a trailing partial character. If
1072 : : * an error occurs then the index of the invalid input is stored here.
1073 : : * It’s guaranteed to be non-negative.
1074 : : * @items_written: (out) (optional): location to store number
1075 : : * of bytes written, or %NULL. The value stored here does not include the
1076 : : * trailing 0 byte. It’s guaranteed to be non-negative.
1077 : : * @error: location to store the error occurring, or %NULL to ignore
1078 : : * errors. Any of the errors in #GConvertError other than
1079 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1080 : : *
1081 : : * Convert a string from UTF-16 to UTF-8. The result will be
1082 : : * terminated with a 0 byte.
1083 : : *
1084 : : * Note that the input is expected to be already in native endianness,
1085 : : * an initial byte-order-mark character is not handled specially.
1086 : : * g_convert() can be used to convert a byte buffer of UTF-16 data of
1087 : : * ambiguous endianness.
1088 : : *
1089 : : * Further note that this function does not validate the result
1090 : : * string; it may e.g. include embedded NUL characters. The only
1091 : : * validation done by this function is to ensure that the input can
1092 : : * be correctly interpreted as UTF-16, i.e. it doesn't contain
1093 : : * unpaired surrogates or partial character sequences.
1094 : : *
1095 : : * Returns: (transfer full): a pointer to a newly allocated UTF-8 string.
1096 : : * This value must be freed with g_free(). If an error occurs,
1097 : : * %NULL will be returned and @error set.
1098 : : **/
1099 : : gchar *
1100 : 140 : g_utf16_to_utf8 (const gunichar2 *str,
1101 : : glong len,
1102 : : glong *items_read,
1103 : : glong *items_written,
1104 : : GError **error)
1105 : : {
1106 : : /* This function and g_utf16_to_ucs4 are almost exactly identical -
1107 : : * The lines that differ are marked.
1108 : : */
1109 : : const gunichar2 *in;
1110 : : gchar *out;
1111 : 140 : gchar *result = NULL;
1112 : : gint n_bytes;
1113 : : gunichar high_surrogate;
1114 : :
1115 : 140 : g_return_val_if_fail (str != NULL, NULL);
1116 : :
1117 : 140 : n_bytes = 0;
1118 : 140 : in = str;
1119 : 140 : high_surrogate = 0;
1120 [ + + + + : 2010 : while ((len < 0 || in - str < len) && *in)
+ + ]
1121 : : {
1122 : 1873 : gunichar2 c = *in;
1123 : : gunichar wc;
1124 : :
1125 [ + + + + ]: 1873 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1126 : : {
1127 [ + + ]: 8 : if (high_surrogate)
1128 : : {
1129 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1130 : 5 : high_surrogate = 0;
1131 : : }
1132 : : else
1133 : : {
1134 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1135 : : _("Invalid sequence in conversion input"));
1136 : 3 : goto err_out;
1137 : : }
1138 : : }
1139 : : else
1140 : : {
1141 [ - + ]: 1865 : if (high_surrogate)
1142 : : {
1143 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1144 : : _("Invalid sequence in conversion input"));
1145 : 0 : goto err_out;
1146 : : }
1147 : :
1148 [ + + + + ]: 1865 : if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1149 : : {
1150 : 11 : high_surrogate = c;
1151 : 11 : goto next1;
1152 : : }
1153 : : else
1154 : 1854 : wc = c;
1155 : : }
1156 : :
1157 : : /********** DIFFERENT for UTF8/UCS4 **********/
1158 [ + + + + : 1859 : n_bytes += UTF8_LENGTH (wc);
+ + - + -
- ]
1159 : :
1160 : 1870 : next1:
1161 : 1870 : in++;
1162 : : }
1163 : :
1164 [ + + + + ]: 137 : if (high_surrogate && !items_read)
1165 : : {
1166 : 4 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1167 : : _("Partial character sequence at end of input"));
1168 : 4 : goto err_out;
1169 : : }
1170 : :
1171 : : /* At this point, everything is valid, and we just need to convert
1172 : : */
1173 : : /********** DIFFERENT for UTF8/UCS4 **********/
1174 : 133 : result = try_malloc_n (n_bytes + 1, 1, error);
1175 [ - + ]: 133 : if (result == NULL)
1176 : 0 : goto err_out;
1177 : :
1178 : 133 : high_surrogate = 0;
1179 : 133 : out = result;
1180 : 133 : in = str;
1181 [ + + ]: 1986 : while (out < result + n_bytes)
1182 : : {
1183 : 1853 : gunichar2 c = *in;
1184 : : gunichar wc;
1185 : :
1186 [ + + + + ]: 1853 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1187 : : {
1188 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1189 : 5 : high_surrogate = 0;
1190 : : }
1191 [ + + + + ]: 1848 : else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1192 : : {
1193 : 5 : high_surrogate = c;
1194 : 5 : goto next2;
1195 : : }
1196 : : else
1197 : 1843 : wc = c;
1198 : :
1199 : : /********** DIFFERENT for UTF8/UCS4 **********/
1200 : 1848 : out += g_unichar_to_utf8 (wc, out);
1201 : :
1202 : 1853 : next2:
1203 : 1853 : in++;
1204 : : }
1205 : :
1206 : : /********** DIFFERENT for UTF8/UCS4 **********/
1207 : 133 : *out = '\0';
1208 : :
1209 [ + + ]: 133 : if (items_written)
1210 : : /********** DIFFERENT for UTF8/UCS4 **********/
1211 : 19 : *items_written = out - result;
1212 : :
1213 : 114 : err_out:
1214 [ + + ]: 140 : if (items_read)
1215 : 21 : *items_read = in - str;
1216 : :
1217 : 140 : return result;
1218 : : }
1219 : :
1220 : : /**
1221 : : * g_utf16_to_ucs4:
1222 : : * @str: (array length=len) (element-type guint16): a UTF-16 encoded string
1223 : : * @len: the maximum length (number of #gunichar2) of @str to use.
1224 : : * If @len < 0, then the string is nul-terminated.
1225 : : * @items_read: (out) (optional): location to store number of
1226 : : * words read, or %NULL. If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will
1227 : : * be returned in case @str contains a trailing partial character. If
1228 : : * an error occurs then the index of the invalid input is stored here.
1229 : : * @items_written: (out) (optional): location to store number
1230 : : * of characters written, or %NULL. The value stored here does not include
1231 : : * the trailing 0 character.
1232 : : * @error: location to store the error occurring, or %NULL to ignore
1233 : : * errors. Any of the errors in #GConvertError other than
1234 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1235 : : *
1236 : : * Convert a string from UTF-16 to UCS-4. The result will be
1237 : : * nul-terminated.
1238 : : *
1239 : : * Returns: (transfer full): a pointer to a newly allocated UCS-4 string.
1240 : : * This value must be freed with g_free(). If an error occurs,
1241 : : * %NULL will be returned and @error set.
1242 : : */
1243 : : gunichar *
1244 : 25 : g_utf16_to_ucs4 (const gunichar2 *str,
1245 : : glong len,
1246 : : glong *items_read,
1247 : : glong *items_written,
1248 : : GError **error)
1249 : : {
1250 : : const gunichar2 *in;
1251 : : gchar *out;
1252 : 25 : gchar *result = NULL;
1253 : : gint n_bytes;
1254 : : gunichar high_surrogate;
1255 : :
1256 : 25 : g_return_val_if_fail (str != NULL, NULL);
1257 : :
1258 : 25 : n_bytes = 0;
1259 : 25 : in = str;
1260 : 25 : high_surrogate = 0;
1261 [ + + + + : 84 : while ((len < 0 || in - str < len) && *in)
+ + ]
1262 : : {
1263 : 62 : gunichar2 c = *in;
1264 : :
1265 [ + + + + ]: 62 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1266 : : {
1267 [ + + ]: 8 : if (high_surrogate)
1268 : : {
1269 : 5 : high_surrogate = 0;
1270 : : }
1271 : : else
1272 : : {
1273 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1274 : : _("Invalid sequence in conversion input"));
1275 : 3 : goto err_out;
1276 : : }
1277 : : }
1278 : : else
1279 : : {
1280 [ - + ]: 54 : if (high_surrogate)
1281 : : {
1282 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1283 : : _("Invalid sequence in conversion input"));
1284 : 0 : goto err_out;
1285 : : }
1286 : :
1287 [ + + + + ]: 54 : if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1288 : : {
1289 : 8 : high_surrogate = c;
1290 : 8 : goto next1;
1291 : : }
1292 : : }
1293 : :
1294 : : /********** DIFFERENT for UTF8/UCS4 **********/
1295 : 51 : n_bytes += sizeof (gunichar);
1296 : :
1297 : 59 : next1:
1298 : 59 : in++;
1299 : : }
1300 : :
1301 [ + + + + ]: 22 : if (high_surrogate && !items_read)
1302 : : {
1303 : 1 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1304 : : _("Partial character sequence at end of input"));
1305 : 1 : goto err_out;
1306 : : }
1307 : :
1308 : : /* At this point, everything is valid, and we just need to convert
1309 : : */
1310 : : /********** DIFFERENT for UTF8/UCS4 **********/
1311 : 21 : result = try_malloc_n (n_bytes + 4, 1, error);
1312 [ - + ]: 21 : if (result == NULL)
1313 : 0 : goto err_out;
1314 : :
1315 : 21 : high_surrogate = 0;
1316 : 21 : out = result;
1317 : 21 : in = str;
1318 [ + + ]: 69 : while (out < result + n_bytes)
1319 : : {
1320 : 48 : gunichar2 c = *in;
1321 : : gunichar wc;
1322 : :
1323 [ + + + + ]: 48 : if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
1324 : : {
1325 : 5 : wc = SURROGATE_VALUE (high_surrogate, c);
1326 : 5 : high_surrogate = 0;
1327 : : }
1328 [ + + + + ]: 43 : else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
1329 : : {
1330 : 5 : high_surrogate = c;
1331 : 5 : goto next2;
1332 : : }
1333 : : else
1334 : 38 : wc = c;
1335 : :
1336 : : /********** DIFFERENT for UTF8/UCS4 **********/
1337 : 43 : *(gunichar *)out = wc;
1338 : 43 : out += sizeof (gunichar);
1339 : :
1340 : 48 : next2:
1341 : 48 : in++;
1342 : : }
1343 : :
1344 : : /********** DIFFERENT for UTF8/UCS4 **********/
1345 : 21 : *(gunichar *)out = 0;
1346 : :
1347 [ + + ]: 21 : if (items_written)
1348 : : /********** DIFFERENT for UTF8/UCS4 **********/
1349 : 19 : *items_written = (out - result) / sizeof (gunichar);
1350 : :
1351 : 2 : err_out:
1352 [ + + ]: 25 : if (items_read)
1353 : 21 : *items_read = in - str;
1354 : :
1355 : 25 : return (gunichar *)result;
1356 : : }
1357 : :
1358 : : /**
1359 : : * g_utf8_to_utf16:
1360 : : * @str: a UTF-8 encoded string
1361 : : * @len: the maximum length (number of bytes) of @str to use.
1362 : : * If @len < 0, then the string is nul-terminated.
1363 : : * @items_read: (out) (optional): location to store number of
1364 : : * bytes read, or %NULL. If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will
1365 : : * be returned in case @str contains a trailing partial character. If
1366 : : * an error occurs then the index of the invalid input is stored here.
1367 : : * @items_written: (out) (optional): location to store number
1368 : : * of #gunichar2 written, or %NULL. The value stored here does not include
1369 : : * the trailing 0.
1370 : : * @error: location to store the error occurring, or %NULL to ignore
1371 : : * errors. Any of the errors in #GConvertError other than
1372 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1373 : : *
1374 : : * Convert a string from UTF-8 to UTF-16. A 0 character will be
1375 : : * added to the result after the converted text.
1376 : : *
1377 : : * Returns: (transfer full): a pointer to a newly allocated UTF-16 string.
1378 : : * This value must be freed with g_free(). If an error occurs,
1379 : : * %NULL will be returned and @error set.
1380 : : */
1381 : : gunichar2 *
1382 : 107 : g_utf8_to_utf16 (const gchar *str,
1383 : : glong len,
1384 : : glong *items_read,
1385 : : glong *items_written,
1386 : : GError **error)
1387 : : {
1388 : 107 : gunichar2 *result = NULL;
1389 : : gint n16;
1390 : : const gchar *in;
1391 : : gint i;
1392 : :
1393 : 107 : g_return_val_if_fail (str != NULL, NULL);
1394 : :
1395 : 107 : in = str;
1396 : 107 : n16 = 0;
1397 [ + + + + : 4426 : while ((len < 0 || str + len - in > 0) && *in)
+ + ]
1398 : : {
1399 [ + + ]: 4325 : gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
1400 [ + + ]: 4325 : if (wc & 0x80000000)
1401 : : {
1402 [ + + ]: 6 : if (wc == (gunichar)-2)
1403 : : {
1404 [ + + ]: 3 : if (items_read)
1405 : 2 : break;
1406 : : else
1407 : 1 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
1408 : : _("Partial character sequence at end of input"));
1409 : : }
1410 : : else
1411 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1412 : : _("Invalid byte sequence in conversion input"));
1413 : :
1414 : 4 : goto err_out;
1415 : : }
1416 : :
1417 [ + + ]: 4319 : if (wc < 0xd800)
1418 : 4311 : n16 += 1;
1419 [ - + ]: 8 : else if (wc < 0xe000)
1420 : : {
1421 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1422 : : _("Invalid sequence in conversion input"));
1423 : :
1424 : 0 : goto err_out;
1425 : : }
1426 [ + + ]: 8 : else if (wc < 0x10000)
1427 : 3 : n16 += 1;
1428 [ + - ]: 5 : else if (wc < 0x110000)
1429 : 5 : n16 += 2;
1430 : : else
1431 : : {
1432 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1433 : : _("Character out of range for UTF-16"));
1434 : :
1435 : 0 : goto err_out;
1436 : : }
1437 : :
1438 : 4319 : in = g_utf8_next_char (in);
1439 : : }
1440 : :
1441 : 103 : result = try_malloc_n (n16 + 1, sizeof (gunichar2), error);
1442 [ - + ]: 103 : if (result == NULL)
1443 : 0 : goto err_out;
1444 : :
1445 : 103 : in = str;
1446 [ + + ]: 4410 : for (i = 0; i < n16;)
1447 : : {
1448 : 4307 : gunichar wc = g_utf8_get_char (in);
1449 : :
1450 [ + + ]: 4307 : if (wc < 0x10000)
1451 : : {
1452 : 4302 : result[i++] = wc;
1453 : : }
1454 : : else
1455 : : {
1456 : 5 : result[i++] = (wc - 0x10000) / 0x400 + 0xd800;
1457 : 5 : result[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
1458 : : }
1459 : :
1460 : 4307 : in = g_utf8_next_char (in);
1461 : : }
1462 : :
1463 : 103 : result[i] = 0;
1464 : :
1465 [ + + ]: 103 : if (items_written)
1466 : 19 : *items_written = n16;
1467 : :
1468 : 84 : err_out:
1469 [ + + ]: 107 : if (items_read)
1470 : 21 : *items_read = in - str;
1471 : :
1472 : 107 : return result;
1473 : : }
1474 : :
1475 : : /**
1476 : : * g_ucs4_to_utf16:
1477 : : * @str: (array length=len) (element-type gunichar): a UCS-4 encoded string
1478 : : * @len: the maximum length (number of characters) of @str to use.
1479 : : * If @len < 0, then the string is nul-terminated.
1480 : : * @items_read: (out) (optional): location to store number of
1481 : : * bytes read, or %NULL. If an error occurs then the index of the invalid
1482 : : * input is stored here.
1483 : : * @items_written: (out) (optional): location to store number
1484 : : * of #gunichar2 written, or %NULL. The value stored here does not include
1485 : : * the trailing 0.
1486 : : * @error: location to store the error occurring, or %NULL to ignore
1487 : : * errors. Any of the errors in #GConvertError other than
1488 : : * %G_CONVERT_ERROR_NO_CONVERSION may occur.
1489 : : *
1490 : : * Convert a string from UCS-4 to UTF-16. A 0 character will be
1491 : : * added to the result after the converted text.
1492 : : *
1493 : : * Returns: (transfer full): a pointer to a newly allocated UTF-16 string.
1494 : : * This value must be freed with g_free(). If an error occurs,
1495 : : * %NULL will be returned and @error set.
1496 : : */
1497 : : gunichar2 *
1498 : 22 : g_ucs4_to_utf16 (const gunichar *str,
1499 : : glong len,
1500 : : glong *items_read,
1501 : : glong *items_written,
1502 : : GError **error)
1503 : : {
1504 : 22 : gunichar2 *result = NULL;
1505 : : gint n16;
1506 : : gint i, j;
1507 : :
1508 : 22 : n16 = 0;
1509 : 22 : i = 0;
1510 [ + + + + : 67 : while ((len < 0 || i < len) && str[i])
+ + ]
1511 : : {
1512 : 48 : gunichar wc = str[i];
1513 : :
1514 [ + + ]: 48 : if (wc < 0xd800)
1515 : 37 : n16 += 1;
1516 [ - + ]: 11 : else if (wc < 0xe000)
1517 : : {
1518 : 0 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1519 : : _("Invalid sequence in conversion input"));
1520 : :
1521 : 0 : goto err_out;
1522 : : }
1523 [ + + ]: 11 : else if (wc < 0x10000)
1524 : 3 : n16 += 1;
1525 [ + + ]: 8 : else if (wc < 0x110000)
1526 : 5 : n16 += 2;
1527 : : else
1528 : : {
1529 : 3 : g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1530 : : _("Character out of range for UTF-16"));
1531 : :
1532 : 3 : goto err_out;
1533 : : }
1534 : :
1535 : 45 : i++;
1536 : : }
1537 : :
1538 : 19 : result = try_malloc_n (n16 + 1, sizeof (gunichar2), error);
1539 [ - + ]: 19 : if (result == NULL)
1540 : 0 : goto err_out;
1541 : :
1542 [ + + ]: 58 : for (i = 0, j = 0; j < n16; i++)
1543 : : {
1544 : 39 : gunichar wc = str[i];
1545 : :
1546 [ + + ]: 39 : if (wc < 0x10000)
1547 : : {
1548 : 34 : result[j++] = wc;
1549 : : }
1550 : : else
1551 : : {
1552 : 5 : result[j++] = (wc - 0x10000) / 0x400 + 0xd800;
1553 : 5 : result[j++] = (wc - 0x10000) % 0x400 + 0xdc00;
1554 : : }
1555 : : }
1556 : 19 : result[j] = 0;
1557 : :
1558 [ + + ]: 19 : if (items_written)
1559 : 17 : *items_written = n16;
1560 : :
1561 : 2 : err_out:
1562 [ + + ]: 22 : if (items_read)
1563 : 19 : *items_read = i;
1564 : :
1565 : 22 : return result;
1566 : : }
1567 : :
1568 : : #define VALIDATE_BYTE(mask, expect) \
1569 : : G_STMT_START { \
1570 : : if (G_UNLIKELY((*(guchar *)p & (mask)) != (expect))) \
1571 : : goto error; \
1572 : : } G_STMT_END
1573 : :
1574 : : /* see IETF RFC 3629 Section 4 */
1575 : :
1576 : : static const gchar *
1577 : 1276949 : fast_validate (const char *str)
1578 : :
1579 : : {
1580 : : const gchar *p;
1581 : :
1582 [ + + ]: 184925487 : for (p = str; *p; p++)
1583 : : {
1584 [ + + ]: 183648820 : if (*(guchar *)p < 128)
1585 : : /* done */;
1586 : : else
1587 : 6292 : {
1588 : : const gchar *last;
1589 : :
1590 : 6574 : last = p;
1591 [ + + ]: 6574 : if (*(guchar *)p < 0xe0) /* 110xxxxx */
1592 : : {
1593 [ + + ]: 1352 : if (G_UNLIKELY (*(guchar *)p < 0xc2))
1594 : 115 : goto error;
1595 : : }
1596 : : else
1597 : : {
1598 [ + + ]: 5222 : if (*(guchar *)p < 0xf0) /* 1110xxxx */
1599 : : {
1600 [ + + + ]: 5142 : switch (*(guchar *)p++ & 0x0f)
1601 : : {
1602 : 334 : case 0:
1603 [ + + ]: 334 : VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
1604 : 324 : break;
1605 : 57 : case 0x0d:
1606 [ + + ]: 57 : VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
1607 : 26 : break;
1608 : 4751 : default:
1609 [ + + ]: 4751 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1610 : : }
1611 : : }
1612 [ + + ]: 80 : else if (*(guchar *)p < 0xf5) /* 11110xxx excluding out-of-range */
1613 : : {
1614 [ + + + ]: 28 : switch (*(guchar *)p++ & 0x07)
1615 : : {
1616 : 18 : case 0:
1617 [ + + ]: 18 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1618 [ + + ]: 16 : if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
1619 : 8 : goto error;
1620 : 8 : break;
1621 : 7 : case 4:
1622 [ + + ]: 7 : VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
1623 : 4 : break;
1624 : 3 : default:
1625 [ + - ]: 3 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1626 : : }
1627 : 12 : p++;
1628 [ - + ]: 12 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1629 : : }
1630 : : else
1631 : 52 : goto error;
1632 : : }
1633 : :
1634 : 6326 : p++;
1635 [ + + ]: 6326 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1636 : :
1637 : 6292 : continue;
1638 : :
1639 : 282 : error:
1640 : 282 : return last;
1641 : : }
1642 : : }
1643 : :
1644 : 1276667 : return p;
1645 : : }
1646 : :
1647 : : static const gchar *
1648 : 1184783 : fast_validate_len (const char *str,
1649 : : gssize max_len)
1650 : :
1651 : : {
1652 : : const gchar *p;
1653 : :
1654 : 1184783 : g_assert (max_len >= 0);
1655 : :
1656 [ + + + + ]: 71340416 : for (p = str; ((p - str) < max_len) && *p; p++)
1657 : : {
1658 [ + + ]: 70156113 : if (*(guchar *)p < 128)
1659 : : /* done */;
1660 : : else
1661 : 4910 : {
1662 : : const gchar *last;
1663 : :
1664 : 5390 : last = p;
1665 [ + + ]: 5390 : if (*(guchar *)p < 0xe0) /* 110xxxxx */
1666 : : {
1667 [ + + ]: 2338 : if (G_UNLIKELY (max_len - (p - str) < 2))
1668 : 94 : goto error;
1669 : :
1670 [ + + ]: 2244 : if (G_UNLIKELY (*(guchar *)p < 0xc2))
1671 : 126 : goto error;
1672 : : }
1673 : : else
1674 : : {
1675 [ + + ]: 3052 : if (*(guchar *)p < 0xf0) /* 1110xxxx */
1676 : : {
1677 [ + + ]: 2956 : if (G_UNLIKELY (max_len - (p - str) < 3))
1678 : 26 : goto error;
1679 : :
1680 [ + + + ]: 2930 : switch (*(guchar *)p++ & 0x0f)
1681 : : {
1682 : 713 : case 0:
1683 [ + + ]: 713 : VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
1684 : 709 : break;
1685 : 67 : case 0x0d:
1686 [ + + ]: 67 : VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
1687 : 51 : break;
1688 : 2150 : default:
1689 [ + + ]: 2150 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1690 : : }
1691 : : }
1692 [ + + ]: 96 : else if (*(guchar *)p < 0xf5) /* 11110xxx excluding out-of-range */
1693 : : {
1694 [ + + ]: 29 : if (G_UNLIKELY (max_len - (p - str) < 4))
1695 : 8 : goto error;
1696 : :
1697 [ + + + ]: 21 : switch (*(guchar *)p++ & 0x07)
1698 : : {
1699 : 14 : case 0:
1700 [ + + ]: 14 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1701 [ + + ]: 12 : if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
1702 : 3 : goto error;
1703 : 9 : break;
1704 : 3 : case 4:
1705 [ + + ]: 3 : VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
1706 : 1 : break;
1707 : 4 : default:
1708 [ + - ]: 4 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1709 : : }
1710 : 10 : p++;
1711 [ - + ]: 10 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1712 : : }
1713 : : else
1714 : 67 : goto error;
1715 : : }
1716 : :
1717 : 5014 : p++;
1718 [ + + ]: 5014 : VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
1719 : :
1720 : 4910 : continue;
1721 : :
1722 : 480 : error:
1723 : 480 : return last;
1724 : : }
1725 : : }
1726 : :
1727 : 1184303 : return p;
1728 : : }
1729 : :
1730 : : /**
1731 : : * g_utf8_validate:
1732 : : * @str: (array length=max_len) (element-type guint8): a pointer to character data
1733 : : * @max_len: max bytes to validate, or -1 to go until NUL
1734 : : * @end: (out) (optional) (transfer none): return location for end of valid data
1735 : : *
1736 : : * Validates UTF-8 encoded text. @str is the text to validate;
1737 : : * if @str is nul-terminated, then @max_len can be -1, otherwise
1738 : : * @max_len should be the number of bytes to validate.
1739 : : * If @end is non-%NULL, then the end of the valid range
1740 : : * will be stored there (i.e. the start of the first invalid
1741 : : * character if some bytes were invalid, or the end of the text
1742 : : * being validated otherwise).
1743 : : *
1744 : : * Note that g_utf8_validate() returns %FALSE if @max_len is
1745 : : * positive and any of the @max_len bytes are nul.
1746 : : *
1747 : : * Returns %TRUE if all of @str was valid. Many GLib and GTK
1748 : : * routines require valid UTF-8 as input; so data read from a file
1749 : : * or the network should be checked with g_utf8_validate() before
1750 : : * doing anything else with it.
1751 : : *
1752 : : * Returns: %TRUE if the text was valid UTF-8
1753 : : */
1754 : : gboolean
1755 : 1280409 : g_utf8_validate (const char *str,
1756 : : gssize max_len,
1757 : : const gchar **end)
1758 : :
1759 : : {
1760 : : const gchar *p;
1761 : :
1762 [ + + ]: 1280409 : if (max_len >= 0)
1763 : 3460 : return g_utf8_validate_len (str, max_len, end);
1764 : :
1765 : 1276949 : p = fast_validate (str);
1766 : :
1767 [ + + ]: 1276949 : if (end)
1768 : 345404 : *end = p;
1769 : :
1770 [ + + ]: 1276949 : if (*p != '\0')
1771 : 282 : return FALSE;
1772 : : else
1773 : 1276667 : return TRUE;
1774 : : }
1775 : :
1776 : : /**
1777 : : * g_utf8_validate_len:
1778 : : * @str: (array length=max_len) (element-type guint8): a pointer to character data
1779 : : * @max_len: max bytes to validate
1780 : : * @end: (out) (optional) (transfer none): return location for end of valid data
1781 : : *
1782 : : * Validates UTF-8 encoded text.
1783 : : *
1784 : : * As with g_utf8_validate(), but @max_len must be set, and hence this function
1785 : : * will always return %FALSE if any of the bytes of @str are nul.
1786 : : *
1787 : : * Returns: %TRUE if the text was valid UTF-8
1788 : : * Since: 2.60
1789 : : */
1790 : : gboolean
1791 : 1184783 : g_utf8_validate_len (const char *str,
1792 : : gsize max_len,
1793 : : const gchar **end)
1794 : :
1795 : : {
1796 : : const gchar *p;
1797 : :
1798 : 1184783 : p = fast_validate_len (str, max_len);
1799 : :
1800 [ + + ]: 1184783 : if (end)
1801 : 1184676 : *end = p;
1802 : :
1803 [ + + ]: 1184783 : if (p != str + max_len)
1804 : 1181388 : return FALSE;
1805 : : else
1806 : 3395 : return TRUE;
1807 : : }
1808 : :
1809 : : /**
1810 : : * g_unichar_validate:
1811 : : * @ch: a Unicode character
1812 : : *
1813 : : * Checks whether @ch is a valid Unicode character. Some possible
1814 : : * integer values of @ch will not be valid. 0 is considered a valid
1815 : : * character, though it's normally a string terminator.
1816 : : *
1817 : : * Returns: %TRUE if @ch is a valid Unicode character
1818 : : **/
1819 : : gboolean
1820 : 10 : g_unichar_validate (gunichar ch)
1821 : : {
1822 [ + + + - ]: 10 : return UNICODE_VALID (ch);
1823 : : }
1824 : :
1825 : : /**
1826 : : * g_utf8_strreverse:
1827 : : * @str: a UTF-8 encoded string
1828 : : * @len: the maximum length of @str to use, in bytes. If @len < 0,
1829 : : * then the string is nul-terminated.
1830 : : *
1831 : : * Reverses a UTF-8 string. @str must be valid UTF-8 encoded text.
1832 : : * (Use g_utf8_validate() on all text before trying to use UTF-8
1833 : : * utility functions with it.)
1834 : : *
1835 : : * This function is intended for programmatic uses of reversed strings.
1836 : : * It pays no attention to decomposed characters, combining marks, byte
1837 : : * order marks, directional indicators (LRM, LRO, etc) and similar
1838 : : * characters which might need special handling when reversing a string
1839 : : * for display purposes.
1840 : : *
1841 : : * Note that unlike g_strreverse(), this function returns
1842 : : * newly-allocated memory, which should be freed with g_free() when
1843 : : * no longer needed.
1844 : : *
1845 : : * Returns: (transfer full): a newly-allocated string which is the reverse of @str
1846 : : *
1847 : : * Since: 2.2
1848 : : */
1849 : : gchar *
1850 : 232 : g_utf8_strreverse (const gchar *str,
1851 : : gssize len)
1852 : : {
1853 : : gchar *r, *result;
1854 : : const gchar *p;
1855 : :
1856 [ + + ]: 232 : if (len < 0)
1857 : 53 : len = strlen (str);
1858 : :
1859 : 232 : result = g_new (gchar, len + 1);
1860 : 232 : r = result + len;
1861 : 232 : p = str;
1862 [ + + ]: 4174 : while (r > result)
1863 : : {
1864 : 3942 : gchar *m, skip = g_utf8_skip[*(guchar*) p];
1865 : 3942 : r -= skip;
1866 : 3942 : g_assert (r >= result);
1867 [ + + ]: 7914 : for (m = r; skip; skip--)
1868 : 3972 : *m++ = *p++;
1869 : : }
1870 : 232 : result[len] = 0;
1871 : :
1872 : 232 : return result;
1873 : : }
1874 : :
1875 : : /**
1876 : : * g_utf8_make_valid:
1877 : : * @str: string to coerce into UTF-8
1878 : : * @len: the maximum length of @str to use, in bytes. If @len < 0,
1879 : : * then the string is nul-terminated.
1880 : : *
1881 : : * If the provided string is valid UTF-8, return a copy of it. If not,
1882 : : * return a copy in which bytes that could not be interpreted as valid Unicode
1883 : : * are replaced with the Unicode replacement character (U+FFFD).
1884 : : *
1885 : : * For example, this is an appropriate function to use if you have received
1886 : : * a string that was incorrectly declared to be UTF-8, and you need a valid
1887 : : * UTF-8 version of it that can be logged or displayed to the user, with the
1888 : : * assumption that it is close enough to ASCII or UTF-8 to be mostly
1889 : : * readable as-is.
1890 : : *
1891 : : * Returns: (transfer full): a valid UTF-8 string whose content resembles @str
1892 : : *
1893 : : * Since: 2.52
1894 : : */
1895 : : gchar *
1896 : 309 : g_utf8_make_valid (const gchar *str,
1897 : : gssize len)
1898 : : {
1899 : : GString *string;
1900 : : const gchar *remainder, *invalid;
1901 : : gsize remaining_bytes, valid_bytes;
1902 : :
1903 : 309 : g_return_val_if_fail (str != NULL, NULL);
1904 : :
1905 [ + + ]: 309 : if (len < 0)
1906 : 298 : len = strlen (str);
1907 : :
1908 : 309 : string = NULL;
1909 : 309 : remainder = str;
1910 : 309 : remaining_bytes = len;
1911 : :
1912 [ + + ]: 378 : while (remaining_bytes != 0)
1913 : : {
1914 [ + + ]: 357 : if (g_utf8_validate (remainder, remaining_bytes, &invalid))
1915 : 288 : break;
1916 : 69 : valid_bytes = invalid - remainder;
1917 : :
1918 [ + + ]: 69 : if (string == NULL)
1919 : 39 : string = g_string_sized_new (remaining_bytes);
1920 : :
1921 [ - + ]: 69 : g_string_append_len (string, remainder, valid_bytes);
1922 : : /* append U+FFFD REPLACEMENT CHARACTER */
1923 [ + - ]: 69 : g_string_append (string, "\357\277\275");
1924 : :
1925 : 69 : remaining_bytes -= valid_bytes + 1;
1926 : 69 : remainder = invalid + 1;
1927 : : }
1928 : :
1929 [ + + ]: 309 : if (string == NULL)
1930 : 270 : return g_strndup (str, len);
1931 : :
1932 [ - + ]: 39 : g_string_append_len (string, remainder, remaining_bytes);
1933 : : g_string_append_c (string, '\0');
1934 : :
1935 : 39 : g_assert (g_utf8_validate (string->str, -1, NULL));
1936 : :
1937 : 39 : return g_string_free (string, FALSE);
1938 : : }
|