LCOV - code coverage report
Current view: top level - glib/glib - gvarianttypeinfo.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 197 198 99.5 %
Date: 2024-04-16 05:15:53 Functions: 24 24 100.0 %
Branches: 62 64 96.9 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright © 2008 Ryan Lortie
       3                 :            :  * Copyright © 2010 Codethink Limited
       4                 :            :  *
       5                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :            :  *
       7                 :            :  * This library is free software; you can redistribute it and/or
       8                 :            :  * modify it under the terms of the GNU Lesser General Public
       9                 :            :  * License as published by the Free Software Foundation; either
      10                 :            :  * version 2.1 of the License, or (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This library is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :            :  * Lesser General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU Lesser General Public
      18                 :            :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19                 :            :  *
      20                 :            :  * Author: Ryan Lortie <desrt@desrt.ca>
      21                 :            :  */
      22                 :            : 
      23                 :            : #include "config.h"
      24                 :            : 
      25                 :            : #include "gvarianttypeinfo.h"
      26                 :            : 
      27                 :            : #include <glib/gtestutils.h>
      28                 :            : #include <glib/gthread.h>
      29                 :            : #include <glib/gslice.h>
      30                 :            : #include <glib/ghash.h>
      31                 :            : #include <glib/grefcount.h>
      32                 :            : 
      33                 :            : #include "glib_trace.h"
      34                 :            : 
      35                 :            : /* < private >
      36                 :            :  * GVariantTypeInfo:
      37                 :            :  *
      38                 :            :  * This structure contains the necessary information to facilitate the
      39                 :            :  * serialization and fast deserialization of a given type of GVariant
      40                 :            :  * value.  A GVariant instance holds a pointer to one of these
      41                 :            :  * structures to provide for efficient operation.
      42                 :            :  *
      43                 :            :  * The GVariantTypeInfo structures for all of the base types, plus the
      44                 :            :  * "variant" type are stored in a read-only static array.
      45                 :            :  *
      46                 :            :  * For container types, a hash table and reference counting is used to
      47                 :            :  * ensure that only one of these structures exists for any given type.
      48                 :            :  * In general, a container GVariantTypeInfo will exist for a given type
      49                 :            :  * only if one or more GVariant instances of that type exist or if
      50                 :            :  * another GVariantTypeInfo has that type as a subtype.  For example, if
      51                 :            :  * a process contains a single GVariant instance with type "(asv)", then
      52                 :            :  * container GVariantTypeInfo structures will exist for "(asv)" and
      53                 :            :  * for "as" (note that "s" and "v" always exist in the static array).
      54                 :            :  *
      55                 :            :  * The trickiest part of GVariantTypeInfo (and in fact, the major reason
      56                 :            :  * for its existence) is the storage of somewhat magical constants that
      57                 :            :  * allow for O(1) lookups of items in tuples.  This is described below.
      58                 :            :  *
      59                 :            :  * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is
      60                 :            :  * contained inside of an ArrayInfo or TupleInfo, respectively.  This
      61                 :            :  * allows the storage of the necessary additional information.
      62                 :            :  *
      63                 :            :  * 'fixed_size' is set to the fixed size of the type, if applicable, or
      64                 :            :  * 0 otherwise (since no type has a fixed size of 0).
      65                 :            :  *
      66                 :            :  * 'alignment' is set to one less than the alignment requirement for
      67                 :            :  * this type.  This makes many operations much more convenient.
      68                 :            :  */
      69                 :            : struct _GVariantTypeInfo
      70                 :            : {
      71                 :            :   gsize fixed_size;
      72                 :            :   guchar alignment;
      73                 :            :   guchar container_class;
      74                 :            : };
      75                 :            : 
      76                 :            : /* Container types are reference counted.  They also need to have their
      77                 :            :  * type string stored explicitly since it is not merely a single letter.
      78                 :            :  */
      79                 :            : typedef struct
      80                 :            : {
      81                 :            :   GVariantTypeInfo info;
      82                 :            : 
      83                 :            :   gchar *type_string;
      84                 :            :   gatomicrefcount ref_count;
      85                 :            : } ContainerInfo;
      86                 :            : 
      87                 :            : /* For 'array' and 'maybe' types, we store some extra information on the
      88                 :            :  * end of the GVariantTypeInfo struct -- the element type (ie: "s" for
      89                 :            :  * "as").  The container GVariantTypeInfo structure holds a reference to
      90                 :            :  * the element typeinfo.
      91                 :            :  */
      92                 :            : typedef struct
      93                 :            : {
      94                 :            :   ContainerInfo container;
      95                 :            : 
      96                 :            :   GVariantTypeInfo *element;
      97                 :            : } ArrayInfo;
      98                 :            : 
      99                 :            : /* For 'tuple' and 'dict entry' types, we store extra information for
     100                 :            :  * each member -- its type and how to find it inside the serialized data
     101                 :            :  * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'.  See the
     102                 :            :  * comment on GVariantMemberInfo in gvarianttypeinfo.h.
     103                 :            :  */
     104                 :            : typedef struct
     105                 :            : {
     106                 :            :   ContainerInfo container;
     107                 :            : 
     108                 :            :   GVariantMemberInfo *members;
     109                 :            :   gsize n_members;
     110                 :            : } TupleInfo;
     111                 :            : 
     112                 :            : 
     113                 :            : /* Hard-code the base types in a constant array */
     114                 :            : static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
     115                 :            : #define fixed_aligned(x)  x, x - 1, 0
     116                 :            : #define not_a_type        0,     0, 0
     117                 :            : #define unaligned         0,     0, 0
     118                 :            : #define aligned(x)        0, x - 1, 0
     119                 :            :   /* 'b' */ { fixed_aligned(1) },   /* boolean */
     120                 :            :   /* 'c' */ { not_a_type },
     121                 :            :   /* 'd' */ { fixed_aligned(8) },   /* double */
     122                 :            :   /* 'e' */ { not_a_type },
     123                 :            :   /* 'f' */ { not_a_type },
     124                 :            :   /* 'g' */ { unaligned        },   /* signature string */
     125                 :            :   /* 'h' */ { fixed_aligned(4) },   /* file handle (int32) */
     126                 :            :   /* 'i' */ { fixed_aligned(4) },   /* int32 */
     127                 :            :   /* 'j' */ { not_a_type },
     128                 :            :   /* 'k' */ { not_a_type },
     129                 :            :   /* 'l' */ { not_a_type },
     130                 :            :   /* 'm' */ { not_a_type },
     131                 :            :   /* 'n' */ { fixed_aligned(2) },   /* int16 */
     132                 :            :   /* 'o' */ { unaligned        },   /* object path string */
     133                 :            :   /* 'p' */ { not_a_type },
     134                 :            :   /* 'q' */ { fixed_aligned(2) },   /* uint16 */
     135                 :            :   /* 'r' */ { not_a_type },
     136                 :            :   /* 's' */ { unaligned        },   /* string */
     137                 :            :   /* 't' */ { fixed_aligned(8) },   /* uint64 */
     138                 :            :   /* 'u' */ { fixed_aligned(4) },   /* uint32 */
     139                 :            :   /* 'v' */ { aligned(8)       },   /* variant */
     140                 :            :   /* 'w' */ { not_a_type },
     141                 :            :   /* 'x' */ { fixed_aligned(8) },   /* int64 */
     142                 :            :   /* 'y' */ { fixed_aligned(1) },   /* byte */
     143                 :            : #undef fixed_aligned
     144                 :            : #undef not_a_type
     145                 :            : #undef unaligned
     146                 :            : #undef aligned
     147                 :            : };
     148                 :            : 
     149                 :            : /* We need to have type strings to return for the base types.  We store
     150                 :            :  * those in another array.  Since all base type strings are single
     151                 :            :  * characters this is easy.  By not storing pointers to strings into the
     152                 :            :  * GVariantTypeInfo itself, we save a bunch of relocations.
     153                 :            :  */
     154                 :            : static const char g_variant_type_info_basic_chars[24][2] = {
     155                 :            :   "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
     156                 :            :   "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
     157                 :            : };
     158                 :            : 
     159                 :            : /* sanity checks to make debugging easier */
     160                 :            : static void
     161                 :   49912356 : g_variant_type_info_check (const GVariantTypeInfo *info,
     162                 :            :                            char                    container_class)
     163                 :            : {
     164                 :            : #ifndef G_DISABLE_ASSERT
     165                 :   49912356 :   g_assert (!container_class || info->container_class == container_class);
     166                 :            : 
     167                 :            :   /* alignment can only be one of these */
     168                 :   49912356 :   g_assert (info->alignment == 0 || info->alignment == 1 ||
     169                 :            :             info->alignment == 3 || info->alignment == 7);
     170                 :            : 
     171         [ +  + ]:   49912356 :   if (info->container_class)
     172                 :            :     {
     173                 :   19851151 :       ContainerInfo *container = (ContainerInfo *) info;
     174                 :            : 
     175                 :            :       /* extra checks for containers */
     176                 :   19851151 :       g_assert (!g_atomic_ref_count_compare (&container->ref_count, 0));
     177                 :   19851151 :       g_assert (container->type_string != NULL);
     178                 :            :     }
     179                 :            :   else
     180                 :            :     {
     181                 :            :       gint index;
     182                 :            : 
     183                 :            :       /* if not a container, then ensure that it is a valid member of
     184                 :            :        * the basic types table
     185                 :            :        */
     186                 :   30061205 :       index = info - g_variant_type_info_basic_table;
     187                 :            : 
     188                 :            :       g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
     189                 :            :       g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24);
     190                 :   30061205 :       g_assert (0 <= index && index < 24);
     191                 :   30061205 :       g_assert (g_variant_type_info_basic_chars[index][0] != ' ');
     192                 :            :     }
     193                 :            : #endif  /* !G_DISABLE_ASSERT */
     194                 :   49912356 : }
     195                 :            : 
     196                 :            : /* < private >
     197                 :            :  * g_variant_type_info_get_type_string:
     198                 :            :  * @info: a #GVariantTypeInfo
     199                 :            :  *
     200                 :            :  * Gets the type string for @info.  The string is nul-terminated.
     201                 :            :  */
     202                 :            : const gchar *
     203                 :   21473354 : g_variant_type_info_get_type_string (GVariantTypeInfo *info)
     204                 :            : {
     205                 :   21473354 :   g_variant_type_info_check (info, 0);
     206                 :            : 
     207         [ +  + ]:   21473354 :   if (info->container_class)
     208                 :            :     {
     209                 :    5578718 :       ContainerInfo *container = (ContainerInfo *) info;
     210                 :            : 
     211                 :            :       /* containers have their type string stored inside them */
     212                 :    5578718 :       return container->type_string;
     213                 :            :     }
     214                 :            :   else
     215                 :            :     {
     216                 :            :       gint index;
     217                 :            : 
     218                 :            :       /* look up the type string in the base type array.  the call to
     219                 :            :        * g_variant_type_info_check() above already ensured validity.
     220                 :            :        */
     221                 :   15894636 :       index = info - g_variant_type_info_basic_table;
     222                 :            : 
     223                 :   15894636 :       return g_variant_type_info_basic_chars[index];
     224                 :            :     }
     225                 :            : }
     226                 :            : 
     227                 :            : /* < private >
     228                 :            :  * g_variant_type_info_query:
     229                 :            :  * @info: a #GVariantTypeInfo
     230                 :            :  * @alignment: (out) (optional): the location to store the alignment, or %NULL
     231                 :            :  * @fixed_size: (out) (optional): the location to store the fixed size, or %NULL
     232                 :            :  *
     233                 :            :  * Queries @info to determine the alignment requirements and fixed size
     234                 :            :  * (if any) of the type.
     235                 :            :  *
     236                 :            :  * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0
     237                 :            :  * to indicate that the type is a variable-sized type.  No type has a
     238                 :            :  * fixed size of 0.
     239                 :            :  *
     240                 :            :  * @alignment, if non-%NULL, is set to one less than the required
     241                 :            :  * alignment of the type.  For example, for a 32bit integer, @alignment
     242                 :            :  * would be set to 3.  This allows you to round an integer up to the
     243                 :            :  * proper alignment by performing the following efficient calculation:
     244                 :            :  *
     245                 :            :  *   offset += ((-offset) & alignment);
     246                 :            :  */
     247                 :            : void
     248                 :   21797981 : g_variant_type_info_query (GVariantTypeInfo *info,
     249                 :            :                            guint            *alignment,
     250                 :            :                            gsize            *fixed_size)
     251                 :            : {
     252         [ +  + ]:   21797981 :   if (alignment)
     253                 :   13459194 :     *alignment = info->alignment;
     254                 :            : 
     255         [ +  + ]:   21797981 :   if (fixed_size)
     256                 :   21186344 :     *fixed_size = info->fixed_size;
     257                 :   21797981 : }
     258                 :            : 
     259                 :            : /* < private >
     260                 :            :  * g_variant_type_info_query_depth:
     261                 :            :  * @info: a #GVariantTypeInfo
     262                 :            :  *
     263                 :            :  * Queries @info to determine the depth of the type.
     264                 :            :  *
     265                 :            :  * See g_variant_type_string_get_depth_() for more details.
     266                 :            :  *
     267                 :            :  * Returns: depth of @info
     268                 :            :  * Since: 2.60
     269                 :            :  */
     270                 :            : gsize
     271                 :     212758 : g_variant_type_info_query_depth (GVariantTypeInfo *info)
     272                 :            : {
     273                 :     212758 :   g_variant_type_info_check (info, 0);
     274                 :            : 
     275         [ +  + ]:     212758 :   if (info->container_class)
     276                 :            :     {
     277                 :      32863 :       ContainerInfo *container = (ContainerInfo *) info;
     278                 :      32863 :       return g_variant_type_string_get_depth_ (container->type_string);
     279                 :            :     }
     280                 :            : 
     281                 :     179895 :   return 1;
     282                 :            : }
     283                 :            : 
     284                 :            : /* == array == */
     285                 :            : #define GV_ARRAY_INFO_CLASS 'a'
     286                 :            : static ArrayInfo *
     287                 :    5680339 : GV_ARRAY_INFO (GVariantTypeInfo *info)
     288                 :            : {
     289                 :    5680339 :   g_variant_type_info_check (info, GV_ARRAY_INFO_CLASS);
     290                 :            : 
     291                 :    5680339 :   return (ArrayInfo *) info;
     292                 :            : }
     293                 :            : 
     294                 :            : static void
     295                 :      59727 : array_info_free (GVariantTypeInfo *info)
     296                 :            : {
     297                 :            :   ArrayInfo *array_info;
     298                 :            : 
     299                 :      59727 :   g_assert (info->container_class == GV_ARRAY_INFO_CLASS);
     300                 :      59727 :   array_info = (ArrayInfo *) info;
     301                 :            : 
     302                 :      59727 :   g_variant_type_info_unref (array_info->element);
     303                 :      59727 :   g_slice_free (ArrayInfo, array_info);
     304                 :      59727 : }
     305                 :            : 
     306                 :            : static ContainerInfo *
     307                 :      59799 : array_info_new (const GVariantType *type)
     308                 :            : {
     309                 :            :   ArrayInfo *info;
     310                 :            : 
     311                 :      59799 :   info = g_slice_new (ArrayInfo);
     312                 :      59799 :   info->container.info.container_class = GV_ARRAY_INFO_CLASS;
     313                 :            : 
     314                 :      59799 :   info->element = g_variant_type_info_get (g_variant_type_element (type));
     315                 :      59799 :   info->container.info.alignment = info->element->alignment;
     316                 :      59799 :   info->container.info.fixed_size = 0;
     317                 :            : 
     318                 :      59799 :   return (ContainerInfo *) info;
     319                 :            : }
     320                 :            : 
     321                 :            : /* < private >
     322                 :            :  * g_variant_type_info_element:
     323                 :            :  * @info: a #GVariantTypeInfo for an array or maybe type
     324                 :            :  *
     325                 :            :  * Returns the element type for the array or maybe type.  A reference is
     326                 :            :  * not added, so the caller must add their own.
     327                 :            :  */
     328                 :            : GVariantTypeInfo *
     329                 :    1424854 : g_variant_type_info_element (GVariantTypeInfo *info)
     330                 :            : {
     331                 :    1424854 :   return GV_ARRAY_INFO (info)->element;
     332                 :            : }
     333                 :            : 
     334                 :            : /* < private >
     335                 :            :  * g_variant_type_query_element:
     336                 :            :  * @info: a #GVariantTypeInfo for an array or maybe type
     337                 :            :  * @alignment: (out) (optional): the location to store the alignment, or %NULL
     338                 :            :  * @fixed_size: (out) (optional): the location to store the fixed size, or %NULL
     339                 :            :  *
     340                 :            :  * Returns the alignment requires and fixed size (if any) for the
     341                 :            :  * element type of the array.  This call is a convenience wrapper around
     342                 :            :  * g_variant_type_info_element() and g_variant_type_info_query().
     343                 :            :  */
     344                 :            : void
     345                 :    4255485 : g_variant_type_info_query_element (GVariantTypeInfo *info,
     346                 :            :                                    guint            *alignment,
     347                 :            :                                    gsize            *fixed_size)
     348                 :            : {
     349                 :    4255485 :   g_variant_type_info_query (GV_ARRAY_INFO (info)->element,
     350                 :            :                              alignment, fixed_size);
     351                 :    4255485 : }
     352                 :            : 
     353                 :            : /* == tuple == */
     354                 :            : #define GV_TUPLE_INFO_CLASS 'r'
     355                 :            : static TupleInfo *
     356                 :    6781522 : GV_TUPLE_INFO (GVariantTypeInfo *info)
     357                 :            : {
     358                 :    6781522 :   g_variant_type_info_check (info, GV_TUPLE_INFO_CLASS);
     359                 :            : 
     360                 :    6781522 :   return (TupleInfo *) info;
     361                 :            : }
     362                 :            : 
     363                 :            : static void
     364                 :      89659 : tuple_info_free (GVariantTypeInfo *info)
     365                 :            : {
     366                 :            :   TupleInfo *tuple_info;
     367                 :            :   gsize i;
     368                 :            : 
     369                 :      89659 :   g_assert (info->container_class == GV_TUPLE_INFO_CLASS);
     370                 :      89659 :   tuple_info = (TupleInfo *) info;
     371                 :            : 
     372         [ +  + ]:     823000 :   for (i = 0; i < tuple_info->n_members; i++)
     373                 :     733341 :     g_variant_type_info_unref (tuple_info->members[i].type_info);
     374                 :            : 
     375                 :      89659 :   g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
     376                 :      89659 :                  tuple_info->members);
     377                 :      89659 :   g_slice_free (TupleInfo, tuple_info);
     378                 :      89659 : }
     379                 :            : 
     380                 :            : static void
     381                 :      89669 : tuple_allocate_members (const GVariantType  *type,
     382                 :            :                         GVariantMemberInfo **members,
     383                 :            :                         gsize               *n_members)
     384                 :            : {
     385                 :            :   const GVariantType *item_type;
     386                 :      89669 :   gsize i = 0;
     387                 :            : 
     388                 :      89669 :   *n_members = g_variant_type_n_items (type);
     389                 :      89669 :   *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
     390                 :            : 
     391                 :      89669 :   item_type = g_variant_type_first (type);
     392         [ +  + ]:     823022 :   while (item_type)
     393                 :            :     {
     394                 :     733353 :       GVariantMemberInfo *member = &(*members)[i++];
     395                 :            : 
     396                 :     733353 :       member->type_info = g_variant_type_info_get (item_type);
     397                 :     733353 :       item_type = g_variant_type_next (item_type);
     398                 :            : 
     399         [ +  + ]:     733353 :       if (member->type_info->fixed_size)
     400                 :     601822 :         member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED;
     401         [ +  + ]:     131531 :       else if (item_type == NULL)
     402                 :      67053 :         member->ending_type = G_VARIANT_MEMBER_ENDING_LAST;
     403                 :            :       else
     404                 :      64478 :         member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET;
     405                 :            :     }
     406                 :            : 
     407                 :      89669 :   g_assert (i == *n_members);
     408                 :      89669 : }
     409                 :            : 
     410                 :            : /* this is g_variant_type_info_query for a given member of the tuple.
     411                 :            :  * before the access is done, it is ensured that the item is within
     412                 :            :  * range and %FALSE is returned if not.
     413                 :            :  */
     414                 :            : static gboolean
     415                 :     823022 : tuple_get_item (TupleInfo          *info,
     416                 :            :                 GVariantMemberInfo *item,
     417                 :            :                 gsize              *d,
     418                 :            :                 gsize              *e)
     419                 :            : {
     420         [ +  + ]:     823022 :   if (&info->members[info->n_members] == item)
     421                 :      89669 :     return FALSE;
     422                 :            : 
     423                 :     733353 :   *d = item->type_info->alignment;
     424                 :     733353 :   *e = item->type_info->fixed_size;
     425                 :     733353 :   return TRUE;
     426                 :            : }
     427                 :            : 
     428                 :            : /* Read the documentation for #GVariantMemberInfo in gvarianttype.h
     429                 :            :  * before attempting to understand this.
     430                 :            :  *
     431                 :            :  * This function adds one set of "magic constant" values (for one item
     432                 :            :  * in the tuple) to the table.
     433                 :            :  *
     434                 :            :  * The algorithm in tuple_generate_table() calculates values of 'a', 'b'
     435                 :            :  * and 'c' for each item, such that the procedure for finding the item
     436                 :            :  * is to start at the end of the previous variable-sized item, add 'a',
     437                 :            :  * then round up to the nearest multiple of 'b', then then add 'c'.
     438                 :            :  * Note that 'b' is stored in the usual "one less than" form.  ie:
     439                 :            :  *
     440                 :            :  *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
     441                 :            :  *
     442                 :            :  * We tweak these values a little to allow for a slightly easier
     443                 :            :  * computation and more compact storage.
     444                 :            :  */
     445                 :            : static void
     446                 :     733353 : tuple_table_append (GVariantMemberInfo **items,
     447                 :            :                     gsize                i,
     448                 :            :                     gsize                a,
     449                 :            :                     gsize                b,
     450                 :            :                     gsize                c)
     451                 :            : {
     452                 :     733353 :   GVariantMemberInfo *item = (*items)++;
     453                 :            : 
     454                 :            :   /* We can shift multiples of the alignment size from 'c' into 'a'.
     455                 :            :    * As long as we're shifting whole multiples, it won't affect the
     456                 :            :    * result.  This means that we can take the "aligned" portion off of
     457                 :            :    * 'c' and add it into 'a'.
     458                 :            :    *
     459                 :            :    *  Imagine (for sake of clarity) that ROUND_10 rounds up to the
     460                 :            :    *  nearest 10.  It is clear that:
     461                 :            :    *
     462                 :            :    *   ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
     463                 :            :    *
     464                 :            :    * ie: remove the 10s portion of 'c' and add it onto 'a'.
     465                 :            :    *
     466                 :            :    * To put some numbers on it, imagine we start with a = 34 and c = 27:
     467                 :            :    *
     468                 :            :    *  ROUND_10(34) + 27 = 40 + 27 = 67
     469                 :            :    *
     470                 :            :    * but also, we can split 27 up into 20 and 7 and do this:
     471                 :            :    *
     472                 :            :    *  ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
     473                 :            :    *                ^^    ^
     474                 :            :    * without affecting the result.  We do that here.
     475                 :            :    *
     476                 :            :    * This reduction in the size of 'c' means that we can store it in a
     477                 :            :    * gchar instead of a gsize.  Due to how the structure is packed, this
     478                 :            :    * ends up saving us 'two pointer sizes' per item in each tuple when
     479                 :            :    * allocating using GSlice.
     480                 :            :    */
     481                 :     733353 :   a += ~b & c;    /* take the "aligned" part of 'c' and add to 'a' */
     482                 :     733353 :   c &= b;         /* chop 'c' to contain only the unaligned part */
     483                 :            : 
     484                 :            : 
     485                 :            :   /* Finally, we made one last adjustment.  Recall:
     486                 :            :    *
     487                 :            :    *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
     488                 :            :    *
     489                 :            :    * Forgetting the '+ c' for the moment:
     490                 :            :    *
     491                 :            :    *   ROUND_UP(prev_end + a, (b + 1));
     492                 :            :    *
     493                 :            :    * we can do a "round up" operation by adding 1 less than the amount
     494                 :            :    * to round up to, then rounding down.  ie:
     495                 :            :    *
     496                 :            :    *   #define ROUND_UP(x, y)    ROUND_DOWN(x + (y-1), y)
     497                 :            :    *
     498                 :            :    * Of course, for rounding down to a power of two, we can just mask
     499                 :            :    * out the appropriate number of low order bits:
     500                 :            :    *
     501                 :            :    *   #define ROUND_DOWN(x, y)  (x & ~(y - 1))
     502                 :            :    *
     503                 :            :    * Which gives us
     504                 :            :    *
     505                 :            :    *   #define ROUND_UP(x, y)    (x + (y - 1) & ~(y - 1))
     506                 :            :    *
     507                 :            :    * but recall that our alignment value 'b' is already "one less".
     508                 :            :    * This means that to round 'prev_end + a' up to 'b' we can just do:
     509                 :            :    *
     510                 :            :    *   ((prev_end + a) + b) & ~b
     511                 :            :    *
     512                 :            :    * Associativity, and putting the 'c' back on:
     513                 :            :    *
     514                 :            :    *   (prev_end + (a + b)) & ~b + c
     515                 :            :    *
     516                 :            :    * Now, since (a + b) is constant, we can just add 'b' to 'a' now and
     517                 :            :    * store that as the number to add to prev_end.  Then we use ~b as the
     518                 :            :    * number to take a bitwise 'and' with.  Finally, 'c' is added on.
     519                 :            :    *
     520                 :            :    * Note, however, that all the low order bits of the 'aligned' value
     521                 :            :    * are masked out and that all of the high order bits of 'c' have been
     522                 :            :    * "moved" to 'a' (in the previous step).  This means that there are
     523                 :            :    * no overlapping bits in the addition -- so we can do a bitwise 'or'
     524                 :            :    * equivalently.
     525                 :            :    *
     526                 :            :    * This means that we can now compute the start address of a given
     527                 :            :    * item in the tuple using the algorithm given in the documentation
     528                 :            :    * for #GVariantMemberInfo:
     529                 :            :    *
     530                 :            :    *   item_start = ((prev_end + a) & b) | c;
     531                 :            :    */
     532                 :            : 
     533                 :     733353 :   item->i = i;
     534                 :     733353 :   item->a = a + b;
     535                 :     733353 :   item->b = ~b;
     536                 :     733353 :   item->c = c;
     537                 :     733353 : }
     538                 :            : 
     539                 :            : static gsize
     540                 :     748406 : tuple_align (gsize offset,
     541                 :            :              guint alignment)
     542                 :            : {
     543                 :     748406 :   return offset + ((-offset) & alignment);
     544                 :            : }
     545                 :            : 
     546                 :            : /* This function is the heart of the algorithm for calculating 'i', 'a',
     547                 :            :  * 'b' and 'c' for each item in the tuple.
     548                 :            :  *
     549                 :            :  * Imagine we want to find the start of the "i" in the type "(su(qx)ni)".
     550                 :            :  * That's a string followed by a uint32, then a tuple containing a
     551                 :            :  * uint16 and an int64, then an int16, then our "i".  In order to get to
     552                 :            :  * our "i" we:
     553                 :            :  *
     554                 :            :  * Start at the end of the string, align to 4 (for the uint32), add 4.
     555                 :            :  * Align to 8, add 16 (for the tuple).  Align to 2, add 2 (for the
     556                 :            :  * int16).  Then we're there.  It turns out that, given 3 simple rules,
     557                 :            :  * we can flatten this iteration into one addition, one alignment, then
     558                 :            :  * one more addition.
     559                 :            :  *
     560                 :            :  * The loop below plays through each item in the tuple, querying its
     561                 :            :  * alignment and fixed_size into 'd' and 'e', respectively.  At all
     562                 :            :  * times the variables 'a', 'b', and 'c' are maintained such that in
     563                 :            :  * order to get to the current point, you add 'a', align to 'b' then add
     564                 :            :  * 'c'.  'b' is kept in "one less than" form.  For each item, the proper
     565                 :            :  * alignment is applied to find the values of 'a', 'b' and 'c' to get to
     566                 :            :  * the start of that item.  Those values are recorded into the table.
     567                 :            :  * The fixed size of the item (if applicable) is then added on.
     568                 :            :  *
     569                 :            :  * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and
     570                 :            :  * addition of fixed size.  They have been proven correct but are
     571                 :            :  * presented here, without proof:
     572                 :            :  *
     573                 :            :  *  1) in order to "align to 'd'" where 'd' is less than or equal to the
     574                 :            :  *     largest level of alignment seen so far ('b'), you align 'c' to
     575                 :            :  *     'd'.
     576                 :            :  *  2) in order to "align to 'd'" where 'd' is greater than the largest
     577                 :            :  *     level of alignment seen so far, you add 'c' aligned to 'b' to the
     578                 :            :  *     value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment
     579                 :            :  *     seen') and reset 'c' to 0.
     580                 :            :  *  3) in order to "add 'e'", just add 'e' to 'c'.
     581                 :            :  */
     582                 :            : static void
     583                 :      89669 : tuple_generate_table (TupleInfo *info)
     584                 :            : {
     585                 :      89669 :   GVariantMemberInfo *items = info->members;
     586                 :      89669 :   gsize i = -1, a = 0, b = 0, c = 0, d, e;
     587                 :            : 
     588                 :            :   /* iterate over each item in the tuple.
     589                 :            :    *   'd' will be the alignment of the item (in one-less form)
     590                 :            :    *   'e' will be the fixed size (or 0 for variable-size items)
     591                 :            :    */
     592         [ +  + ]:     823022 :   while (tuple_get_item (info, items, &d, &e))
     593                 :            :     {
     594                 :            :       /* align to 'd' */
     595         [ +  + ]:     733353 :       if (d <= b)
     596                 :     612580 :         c = tuple_align (c, d);                   /* rule 1 */
     597                 :            :       else
     598                 :     120773 :         a += tuple_align (c, b), b = d, c = 0;    /* rule 2 */
     599                 :            : 
     600                 :            :       /* the start of the item is at this point (ie: right after we
     601                 :            :        * have aligned for it).  store this information in the table.
     602                 :            :        */
     603                 :     733353 :       tuple_table_append (&items, i, a, b, c);
     604                 :            : 
     605                 :            :       /* "move past" the item by adding in its size. */
     606         [ +  + ]:     733353 :       if (e == 0)
     607                 :            :         /* variable size:
     608                 :            :          *
     609                 :            :          * we'll have an offset stored to mark the end of this item, so
     610                 :            :          * just bump the offset index to give us a new starting point
     611                 :            :          * and reset all the counters.
     612                 :            :          */
     613                 :     131531 :         i++, a = b = c = 0;
     614                 :            :       else
     615                 :            :         /* fixed size */
     616                 :     601822 :         c += e;                                   /* rule 3 */
     617                 :            :     }
     618                 :      89669 : }
     619                 :            : 
     620                 :            : static void
     621                 :      89669 : tuple_set_base_info (TupleInfo *info)
     622                 :            : {
     623                 :      89669 :   GVariantTypeInfo *base = &info->container.info;
     624                 :            : 
     625         [ +  + ]:      89669 :   if (info->n_members > 0)
     626                 :            :     {
     627                 :            :       GVariantMemberInfo *m;
     628                 :            : 
     629                 :            :       /* the alignment requirement of the tuple is the alignment
     630                 :            :        * requirement of its largest item.
     631                 :            :        */
     632                 :      88606 :       base->alignment = 0;
     633         [ +  + ]:     821959 :       for (m = info->members; m < &info->members[info->n_members]; m++)
     634                 :            :         /* can find the max of a list of "one less than" powers of two
     635                 :            :          * by 'or'ing them
     636                 :            :          */
     637                 :     733353 :         base->alignment |= m->type_info->alignment;
     638                 :            : 
     639                 :      88606 :       m--; /* take 'm' back to the last item */
     640                 :            : 
     641                 :            :       /* the structure only has a fixed size if no variable-size
     642                 :            :        * offsets are stored and the last item is fixed-sized too (since
     643                 :            :        * an offset is never stored for the last item).
     644                 :            :        */
     645   [ +  +  +  + ]:      88606 :       if (m->i == (gsize) -1 && m->type_info->fixed_size)
     646                 :            :         /* in that case, the fixed size can be found by finding the
     647                 :            :          * start of the last item (in the usual way) and adding its
     648                 :            :          * fixed size.
     649                 :            :          *
     650                 :            :          * if a tuple has a fixed size then it is always a multiple of
     651                 :            :          * the alignment requirement (to make packing into arrays
     652                 :            :          * easier) so we round up to that here.
     653                 :            :          */
     654                 :      15053 :         base->fixed_size =
     655                 :      15053 :           tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size,
     656                 :      15053 :                        base->alignment);
     657                 :            :       else
     658                 :            :         /* else, the tuple is not fixed size */
     659                 :      73553 :         base->fixed_size = 0;
     660                 :            :     }
     661                 :            :   else
     662                 :            :     {
     663                 :            :       /* the empty tuple: '()'.
     664                 :            :        *
     665                 :            :        * has a size of 1 and a no alignment requirement.
     666                 :            :        *
     667                 :            :        * It has a size of 1 (not 0) for two practical reasons:
     668                 :            :        *
     669                 :            :        *  1) So we can determine how many of them are in an array
     670                 :            :        *     without dividing by zero or without other tricks.
     671                 :            :        *
     672                 :            :        *  2) Even if we had some trick to know the number of items in
     673                 :            :        *     the array (as GVariant did at one time) this would open a
     674                 :            :        *     potential denial of service attack: an attacker could send
     675                 :            :        *     you an extremely small array (in terms of number of bytes)
     676                 :            :        *     containing trillions of zero-sized items.  If you iterated
     677                 :            :        *     over this array you would effectively infinite-loop your
     678                 :            :        *     program.  By forcing a size of at least one, we bound the
     679                 :            :        *     amount of computation done in response to a message to a
     680                 :            :        *     reasonable function of the size of that message.
     681                 :            :        */
     682                 :       1063 :       base->alignment = 0;
     683                 :       1063 :       base->fixed_size = 1;
     684                 :            :     }
     685                 :      89669 : }
     686                 :            : 
     687                 :            : static ContainerInfo *
     688                 :      89669 : tuple_info_new (const GVariantType *type)
     689                 :            : {
     690                 :            :   TupleInfo *info;
     691                 :            : 
     692                 :      89669 :   info = g_slice_new (TupleInfo);
     693                 :      89669 :   info->container.info.container_class = GV_TUPLE_INFO_CLASS;
     694                 :            : 
     695                 :      89669 :   tuple_allocate_members (type, &info->members, &info->n_members);
     696                 :      89669 :   tuple_generate_table (info);
     697                 :      89669 :   tuple_set_base_info (info);
     698                 :            : 
     699                 :      89669 :   return (ContainerInfo *) info;
     700                 :            : }
     701                 :            : 
     702                 :            : /* < private >
     703                 :            :  * g_variant_type_info_n_members:
     704                 :            :  * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
     705                 :            :  *
     706                 :            :  * Returns the number of members in a tuple or dictionary entry type.
     707                 :            :  * For a dictionary entry this will always be 2.
     708                 :            :  */
     709                 :            : gsize
     710                 :    1042792 : g_variant_type_info_n_members (GVariantTypeInfo *info)
     711                 :            : {
     712                 :    1042792 :   return GV_TUPLE_INFO (info)->n_members;
     713                 :            : }
     714                 :            : 
     715                 :            : /* < private >
     716                 :            :  * g_variant_type_info_member_info:
     717                 :            :  * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
     718                 :            :  * @index: the member to fetch information for
     719                 :            :  *
     720                 :            :  * Returns the #GVariantMemberInfo for a given member.  See
     721                 :            :  * documentation for that structure for why you would want this
     722                 :            :  * information.
     723                 :            :  *
     724                 :            :  * @index must refer to a valid child (ie: strictly less than
     725                 :            :  * g_variant_type_info_n_members() returns).
     726                 :            :  */
     727                 :            : const GVariantMemberInfo *
     728                 :    5738730 : g_variant_type_info_member_info (GVariantTypeInfo *info,
     729                 :            :                                  gsize             index)
     730                 :            : {
     731                 :    5738730 :   TupleInfo *tuple_info = GV_TUPLE_INFO (info);
     732                 :            : 
     733         [ +  - ]:    5738730 :   if (index < tuple_info->n_members)
     734                 :    5738730 :     return &tuple_info->members[index];
     735                 :            : 
     736                 :          0 :   return NULL;
     737                 :            : }
     738                 :            : 
     739                 :            : /* == new/ref/unref == */
     740                 :            : static GRecMutex g_variant_type_info_lock;
     741                 :            : static GHashTable *g_variant_type_info_table;
     742                 :            : 
     743                 :            : /* < private >
     744                 :            :  * g_variant_type_info_get:
     745                 :            :  * @type: a #GVariantType
     746                 :            :  *
     747                 :            :  * Returns a reference to a #GVariantTypeInfo for @type.
     748                 :            :  *
     749                 :            :  * If an info structure already exists for this type, a new reference is
     750                 :            :  * returned.  If not, the required calculations are performed and a new
     751                 :            :  * info structure is returned.
     752                 :            :  *
     753                 :            :  * It is appropriate to call g_variant_type_info_unref() on the return
     754                 :            :  * value.
     755                 :            :  */
     756                 :            : GVariantTypeInfo *
     757                 :    5861525 : g_variant_type_info_get (const GVariantType *type)
     758                 :            : {
     759                 :            :   char type_char;
     760                 :            : 
     761                 :    5861525 :   type_char = g_variant_type_peek_string (type)[0];
     762                 :            : 
     763   [ +  +  +  + ]:    5861525 :   if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
     764         [ +  + ]:    5700913 :       type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY ||
     765         [ +  + ]:    5612208 :       type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE ||
     766                 :            :       type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY)
     767                 :            :     {
     768                 :            :       GVariantTypeInfo *info;
     769                 :            :       gchar *type_string;
     770                 :            : 
     771                 :     518937 :       type_string = g_variant_type_dup_string (type);
     772                 :            : 
     773                 :     518937 :       g_rec_mutex_lock (&g_variant_type_info_lock);
     774                 :            : 
     775         [ +  + ]:     518937 :       if (g_variant_type_info_table == NULL)
     776                 :      34732 :         g_variant_type_info_table = g_hash_table_new (g_str_hash,
     777                 :            :                                                       g_str_equal);
     778                 :     518937 :       info = g_hash_table_lookup (g_variant_type_info_table, type_string);
     779                 :            : 
     780         [ +  + ]:     518937 :       if (info == NULL)
     781                 :            :         {
     782                 :            :           ContainerInfo *container;
     783                 :            : 
     784   [ +  +  +  + ]:     149468 :           if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
     785                 :            :               type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
     786                 :            :             {
     787                 :      59799 :               container = array_info_new (type);
     788                 :            :             }
     789                 :            :           else /* tuple or dict entry */
     790                 :            :             {
     791                 :      89669 :               container = tuple_info_new (type);
     792                 :            :             }
     793                 :            : 
     794                 :     149468 :           info = (GVariantTypeInfo *) container;
     795                 :     149468 :           container->type_string = type_string;
     796                 :     149468 :           g_atomic_ref_count_init (&container->ref_count);
     797                 :            : 
     798                 :     149468 :           TRACE(GLIB_VARIANT_TYPE_INFO_NEW(info, container->type_string));
     799                 :            : 
     800                 :     149468 :           g_hash_table_insert (g_variant_type_info_table, type_string, info);
     801                 :     149468 :           type_string = NULL;
     802                 :            :         }
     803                 :            :       else
     804                 :     369469 :         g_variant_type_info_ref (info);
     805                 :            : 
     806                 :     518937 :       g_rec_mutex_unlock (&g_variant_type_info_lock);
     807                 :     518937 :       g_variant_type_info_check (info, 0);
     808                 :     518937 :       g_free (type_string);
     809                 :            : 
     810                 :     518937 :       return info;
     811                 :            :     }
     812                 :            :   else
     813                 :            :     {
     814                 :            :       const GVariantTypeInfo *info;
     815                 :            :       int index;
     816                 :            : 
     817                 :    5342588 :       index = type_char - 'b';
     818                 :            :       g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
     819                 :    5342588 :       g_assert_cmpint (0, <=, index);
     820                 :    5342588 :       g_assert_cmpint (index, <, 24);
     821                 :            : 
     822                 :    5342588 :       info = g_variant_type_info_basic_table + index;
     823                 :    5342588 :       g_variant_type_info_check (info, 0);
     824                 :            : 
     825                 :    5342588 :       TRACE(GLIB_VARIANT_TYPE_INFO_NEW(info, g_variant_type_info_basic_chars[index]));
     826                 :            : 
     827                 :    5342588 :       return (GVariantTypeInfo *) info;
     828                 :            :     }
     829                 :            : }
     830                 :            : 
     831                 :            : /* < private >
     832                 :            :  * g_variant_type_info_ref:
     833                 :            :  * @info: a #GVariantTypeInfo
     834                 :            :  *
     835                 :            :  * Adds a reference to @info.
     836                 :            :  */
     837                 :            : GVariantTypeInfo *
     838                 :    2205607 : g_variant_type_info_ref (GVariantTypeInfo *info)
     839                 :            : {
     840                 :    2205607 :   g_variant_type_info_check (info, 0);
     841                 :            : 
     842         [ +  + ]:    2205607 :   if (info->container_class)
     843                 :            :     {
     844                 :     554695 :       ContainerInfo *container = (ContainerInfo *) info;
     845                 :            : 
     846                 :     554695 :       g_atomic_ref_count_inc (&container->ref_count);
     847                 :            :     }
     848                 :            : 
     849                 :    2205607 :   return info;
     850                 :            : }
     851                 :            : 
     852                 :            : /* < private >
     853                 :            :  * g_variant_type_info_unref:
     854                 :            :  * @info: a #GVariantTypeInfo
     855                 :            :  *
     856                 :            :  * Releases a reference held on @info.  This may result in @info being
     857                 :            :  * freed.
     858                 :            :  */
     859                 :            : void
     860                 :    7697251 : g_variant_type_info_unref (GVariantTypeInfo *info)
     861                 :            : {
     862                 :    7697251 :   g_variant_type_info_check (info, 0);
     863                 :            : 
     864         [ +  + ]:    7697251 :   if (info->container_class)
     865                 :            :     {
     866                 :     704077 :       ContainerInfo *container = (ContainerInfo *) info;
     867                 :            : 
     868                 :     704077 :       g_rec_mutex_lock (&g_variant_type_info_lock);
     869         [ +  + ]:     704077 :       if (g_atomic_ref_count_dec (&container->ref_count))
     870                 :            :         {
     871                 :            : 
     872                 :     149386 :           TRACE(GLIB_VARIANT_TYPE_INFO_FREE(info));
     873                 :            : 
     874                 :     149386 :           g_hash_table_remove (g_variant_type_info_table,
     875                 :     149386 :                                container->type_string);
     876         [ +  + ]:     149386 :           if (g_hash_table_size (g_variant_type_info_table) == 0)
     877                 :            :             {
     878                 :      34718 :               g_hash_table_unref (g_variant_type_info_table);
     879                 :      34718 :               g_variant_type_info_table = NULL;
     880                 :            :             }
     881                 :     149386 :           g_rec_mutex_unlock (&g_variant_type_info_lock);
     882                 :            : 
     883                 :     149386 :           g_free (container->type_string);
     884                 :            : 
     885         [ +  + ]:     149386 :           if (info->container_class == GV_ARRAY_INFO_CLASS)
     886                 :      59727 :             array_info_free (info);
     887                 :            : 
     888         [ +  - ]:      89659 :           else if (info->container_class == GV_TUPLE_INFO_CLASS)
     889                 :      89659 :             tuple_info_free (info);
     890                 :            : 
     891                 :            :           else
     892                 :            :             g_assert_not_reached ();
     893                 :            :         }
     894                 :            :       else
     895                 :     554691 :         g_rec_mutex_unlock (&g_variant_type_info_lock);
     896                 :            :     }
     897                 :    7697251 : }
     898                 :            : 
     899                 :            : void
     900                 :         17 : g_variant_type_info_assert_no_infos (void)
     901                 :            : {
     902                 :         17 :   g_assert (g_variant_type_info_table == NULL);
     903                 :         17 : }

Generated by: LCOV version 1.14