LCOV - code coverage report
Current view: top level - glib/glib - gutf8.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 578 610 94.8 %
Date: 2024-04-23 05:16:05 Functions: 30 30 100.0 %
Branches: 417 456 91.4 %

           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                 :            : }

Generated by: LCOV version 1.14