LCOV - code coverage report
Current view: top level - glib/glib - gdataset.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 368 379 97.1 %
Date: 2024-04-16 05:15:53 Functions: 30 30 100.0 %
Branches: 178 216 82.4 %

           Branch data     Line data    Source code
       1                 :            : /* GLIB - Library of useful routines for C programming
       2                 :            :  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
       3                 :            :  *
       4                 :            :  * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
       5                 :            :  * Copyright (C) 1998 Tim Janik
       6                 :            :  *
       7                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
       8                 :            :  *
       9                 :            :  * This library is free software; you can redistribute it and/or
      10                 :            :  * modify it under the terms of the GNU Lesser General Public
      11                 :            :  * License as published by the Free Software Foundation; either
      12                 :            :  * version 2.1 of the License, or (at your option) any later version.
      13                 :            :  *
      14                 :            :  * This library is distributed in the hope that it will be useful,
      15                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17                 :            :  * Lesser General Public License for more details.
      18                 :            :  *
      19                 :            :  * You should have received a copy of the GNU Lesser General Public
      20                 :            :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      21                 :            :  */
      22                 :            : 
      23                 :            : /*
      24                 :            :  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
      25                 :            :  * file for a list of people on the GLib Team.  See the ChangeLog
      26                 :            :  * files for a list of changes.  These files are distributed with
      27                 :            :  * GLib at ftp://ftp.gtk.org/pub/gtk/.
      28                 :            :  */
      29                 :            : 
      30                 :            : /*
      31                 :            :  * MT safe ; except for g_data*_foreach()
      32                 :            :  */
      33                 :            : 
      34                 :            : #include "config.h"
      35                 :            : 
      36                 :            : #include <string.h>
      37                 :            : 
      38                 :            : #include "gdataset.h"
      39                 :            : #include "gbitlock.h"
      40                 :            : 
      41                 :            : #include "gslice.h"
      42                 :            : #include "gdatasetprivate.h"
      43                 :            : #include "gutilsprivate.h"
      44                 :            : #include "ghash.h"
      45                 :            : #include "gquark.h"
      46                 :            : #include "gstrfuncs.h"
      47                 :            : #include "gtestutils.h"
      48                 :            : #include "gthread.h"
      49                 :            : #include "glib_trace.h"
      50                 :            : #include "galloca.h"
      51                 :            : 
      52                 :            : /**
      53                 :            :  * GData:
      54                 :            :  *
      55                 :            :  * An opaque data structure that represents a keyed data list.
      56                 :            :  *
      57                 :            :  * See also: [Keyed data lists](datalist-and-dataset.html).
      58                 :            :  **/
      59                 :            : 
      60                 :            : /**
      61                 :            :  * GDestroyNotify:
      62                 :            :  * @data: the data element.
      63                 :            :  *
      64                 :            :  * Specifies the type of function which is called when a data element
      65                 :            :  * is destroyed. It is passed the pointer to the data element and
      66                 :            :  * should free any memory and resources allocated for it.
      67                 :            :  **/
      68                 :            : 
      69                 :            : #define G_DATALIST_FLAGS_MASK_INTERNAL 0x7
      70                 :            : 
      71                 :            : #define G_DATALIST_CLEAN_POINTER(ptr) \
      72                 :            :   ((GData *) ((gpointer) (((guintptr) (ptr)) & ~((guintptr) G_DATALIST_FLAGS_MASK_INTERNAL))))
      73                 :            : 
      74                 :            : /* datalist pointer accesses have to be carried out atomically */
      75                 :            : #define G_DATALIST_GET_POINTER(datalist) \
      76                 :            :   G_DATALIST_CLEAN_POINTER (g_atomic_pointer_get (datalist))
      77                 :            : 
      78                 :            : #define G_DATALIST_SET_POINTER(datalist, pointer)       G_STMT_START {                           \
      79                 :            :   gpointer _oldv = g_atomic_pointer_get (datalist);                                              \
      80                 :            :   gpointer _newv;                                                                                \
      81                 :            :   do {                                                                                           \
      82                 :            :     _newv = (gpointer) (((guintptr) _oldv & ((guintptr) G_DATALIST_FLAGS_MASK_INTERNAL)) | (guintptr) pointer); \
      83                 :            :   } while (!g_atomic_pointer_compare_and_exchange_full ((void**) datalist, _oldv,                \
      84                 :            :                                                         _newv, &_oldv));                         \
      85                 :            : } G_STMT_END
      86                 :            : 
      87                 :            : /* --- structures --- */
      88                 :            : typedef struct {
      89                 :            :   GQuark          key;
      90                 :            :   gpointer        data;
      91                 :            :   GDestroyNotify  destroy;
      92                 :            : } GDataElt;
      93                 :            : 
      94                 :            : typedef struct _GDataset GDataset;
      95                 :            : struct _GData
      96                 :            : {
      97                 :            :   guint32  len;     /* Number of elements */
      98                 :            :   guint32  alloc;   /* Number of allocated elements */
      99                 :            :   GDataElt data[1]; /* Flexible array */
     100                 :            : };
     101                 :            : 
     102                 :            : struct _GDataset
     103                 :            : {
     104                 :            :   gconstpointer location;
     105                 :            :   GData        *datalist;
     106                 :            : };
     107                 :            : 
     108                 :            : 
     109                 :            : /* --- prototypes --- */
     110                 :            : static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
     111                 :            : static void             g_dataset_destroy_internal      (GDataset        *dataset);
     112                 :            : static inline gpointer  g_data_set_internal             (GData          **datalist,
     113                 :            :                                                          GQuark           key_id,
     114                 :            :                                                          gpointer         data,
     115                 :            :                                                          GDestroyNotify   destroy_func,
     116                 :            :                                                          GDataset        *dataset);
     117                 :            : static void             g_data_initialize               (void);
     118                 :            : 
     119                 :            : /* Locking model:
     120                 :            :  * Each standalone GDataList is protected by a bitlock in the datalist pointer,
     121                 :            :  * which protects that modification of the non-flags part of the datalist pointer
     122                 :            :  * and the contents of the datalist.
     123                 :            :  *
     124                 :            :  * For GDataSet we have a global lock g_dataset_global that protects
     125                 :            :  * the global dataset hash and cache, and additionally it protects the
     126                 :            :  * datalist such that we can avoid to use the bit lock in a few places
     127                 :            :  * where it is easy.
     128                 :            :  */
     129                 :            : 
     130                 :            : /* --- variables --- */
     131                 :            : G_LOCK_DEFINE_STATIC (g_dataset_global);
     132                 :            : static GHashTable   *g_dataset_location_ht = NULL;
     133                 :            : static GDataset     *g_dataset_cached = NULL; /* should this be
     134                 :            :                                                  thread specific? */
     135                 :            : 
     136                 :            : /* --- functions --- */
     137                 :            : 
     138                 :            : #define DATALIST_LOCK_BIT 2
     139                 :            : 
     140                 :            : G_ALWAYS_INLINE static inline GData *
     141                 :            : g_datalist_lock_and_get (GData **datalist)
     142                 :            : {
     143                 :            :   guintptr ptr;
     144                 :            : 
     145                 :    3707373 :   g_pointer_bit_lock_and_get ((void **) datalist, DATALIST_LOCK_BIT, &ptr);
     146                 :   54068706 :   return G_DATALIST_CLEAN_POINTER (ptr);
     147                 :            : }
     148                 :            : 
     149                 :            : static void
     150                 :   40056591 : g_datalist_unlock (GData **datalist)
     151                 :            : {
     152                 :   40056591 :   g_pointer_bit_unlock ((void **)datalist, DATALIST_LOCK_BIT);
     153                 :   40056591 : }
     154                 :            : 
     155                 :            : static void
     156                 :   14012115 : g_datalist_unlock_and_set (GData **datalist, gpointer ptr)
     157                 :            : {
     158                 :   14012115 :   g_pointer_bit_unlock_and_set ((void **) datalist, DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL);
     159                 :   14012115 : }
     160                 :            : 
     161                 :            : static gboolean
     162                 :    7074080 : datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
     163                 :            : {
     164                 :            :   gboolean reallocated;
     165                 :            :   GData *d;
     166                 :            : 
     167                 :    7074080 :   d = *data;
     168         [ +  + ]:    7074080 :   if (!d)
     169                 :            :     {
     170                 :    7005561 :       d = g_malloc (G_STRUCT_OFFSET (GData, data) + 2u * sizeof (GDataElt));
     171                 :    7005561 :       d->len = 0;
     172                 :    7005561 :       d->alloc = 2u;
     173                 :    7005561 :       *data = d;
     174                 :    7005561 :       reallocated = TRUE;
     175                 :            :     }
     176         [ +  + ]:      68519 :   else if (d->len == d->alloc)
     177                 :            :     {
     178                 :       1299 :       d->alloc = d->alloc * 2u;
     179                 :            : #if G_ENABLE_DEBUG
     180                 :            :       /* d->alloc is always a power of two. It thus overflows the first time
     181                 :            :        * when going to (G_MAXUINT32+1), or when requesting 2^31+1 elements.
     182                 :            :        *
     183                 :            :        * This is not handled, and we just crash. That's because we track the GData
     184                 :            :        * in a linear list, which horribly degrades long before we add 2 billion entries.
     185                 :            :        * Don't ever try to do that. */
     186                 :       1299 :       g_assert (d->alloc > d->len);
     187                 :            : #endif
     188                 :       1299 :       d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + d->alloc * sizeof (GDataElt));
     189                 :       1299 :       *data = d;
     190                 :       1299 :       reallocated = TRUE;
     191                 :            :     }
     192                 :            :   else
     193                 :      67220 :     reallocated = FALSE;
     194                 :            : 
     195                 :    7074080 :   d->data[d->len] = (GDataElt){
     196                 :            :     .key = key_id,
     197                 :            :     .data = new_data,
     198                 :            :     .destroy = destroy_func,
     199                 :            :   };
     200                 :    7074080 :   d->len++;
     201                 :            : 
     202                 :    7074080 :   return reallocated;
     203                 :            : }
     204                 :            : 
     205                 :            : static void
     206                 :    3596345 : datalist_remove (GData *data, guint32 idx)
     207                 :            : {
     208                 :            : #if G_ENABLE_DEBUG
     209                 :    3596345 :   g_assert (idx < data->len);
     210                 :            : #endif
     211                 :            : 
     212                 :            :   /* g_data_remove_internal() relies on the fact, that this function removes
     213                 :            :    * the entry similar to g_array_remove_index_fast(). That is, the entries up
     214                 :            :    * to @idx are left unchanged, and the last entry is moved to position @idx.
     215                 :            :    * */
     216                 :            : 
     217                 :    3596345 :   data->len--;
     218                 :            : 
     219         [ +  + ]:    3596345 :   if (idx != data->len)
     220                 :       2828 :     data->data[idx] = data->data[data->len];
     221                 :    3596345 : }
     222                 :            : 
     223                 :            : static gboolean
     224                 :    3595642 : datalist_shrink (GData **data, GData **d_to_free)
     225                 :            : {
     226                 :            :   guint32 alloc_by_4;
     227                 :            :   guint32 v;
     228                 :            :   GData *d;
     229                 :            : 
     230                 :    3595642 :   d = *data;
     231                 :            : 
     232                 :    3595642 :   alloc_by_4 = d->alloc / 4u;
     233                 :            : 
     234         [ +  + ]:    3595642 :   if (G_LIKELY (d->len > alloc_by_4))
     235                 :            :     {
     236                 :            :       /* No shrinking */
     237                 :       2608 :       return FALSE;
     238                 :            :     }
     239                 :            : 
     240         [ +  + ]:    3593034 :   if (d->len == 0)
     241                 :            :     {
     242                 :            :       /* The list became empty. We drop the allocated memory altogether. */
     243                 :            : 
     244                 :            :       /* The caller will free the buffer after releasing the lock, to minimize
     245                 :            :        * the time we hold the lock. Transfer it out. */
     246                 :    3592997 :       *d_to_free = d;
     247                 :    3592997 :       *data = NULL;
     248                 :    3592997 :       return TRUE;
     249                 :            :     }
     250                 :            : 
     251                 :            :   /* If the buffer is filled not more than 25%. Shrink to double the current length. */
     252                 :            : 
     253                 :         37 :   v = d->len;
     254         [ -  + ]:         37 :   if (v != alloc_by_4)
     255                 :            :     {
     256                 :            :       /* d->alloc is a power of two. Usually, we remove one element at a
     257                 :            :        * time, then we will just reach reach a quarter of that.
     258                 :            :        *
     259                 :            :        * However, with g_datalist_id_remove_multiple(), len can be smaller
     260                 :            :        * at once. In that case, find first the next power of two. */
     261                 :          0 :       v = g_nearest_pow (v);
     262                 :            :     }
     263                 :         37 :   v *= 2u;
     264                 :            : 
     265                 :            : #if G_ENABLE_DEBUG
     266                 :         37 :   g_assert (v > d->len);
     267                 :         37 :   g_assert (v <= d->alloc / 2u);
     268                 :            : #endif
     269                 :            : 
     270                 :         37 :   d->alloc = v;
     271                 :         37 :   d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + (v * sizeof (GDataElt)));
     272                 :         37 :   *d_to_free = NULL;
     273                 :         37 :   *data = d;
     274                 :         37 :   return TRUE;
     275                 :            : }
     276                 :            : 
     277                 :            : static GDataElt *
     278                 :   50480885 : datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
     279                 :            : {
     280                 :            :   guint32 i;
     281                 :            : 
     282         [ +  + ]:   50480885 :   if (data)
     283                 :            :     {
     284         [ +  + ]:   54730294 :       for (i = 0; i < data->len; i++)
     285                 :            :         {
     286                 :   40945971 :           GDataElt *data_elt = &data->data[i];
     287                 :            : 
     288         [ +  + ]:   40945971 :           if (data_elt->key == key_id)
     289                 :            :             {
     290         [ +  + ]:   21559097 :               if (out_idx)
     291                 :    4435111 :                 *out_idx = i;
     292                 :   21559097 :               return data_elt;
     293                 :            :             }
     294                 :            :         }
     295                 :            :     }
     296         [ +  + ]:   28921788 :   if (out_idx)
     297                 :   20825696 :     *out_idx = G_MAXUINT32;
     298                 :   28921788 :   return NULL;
     299                 :            : }
     300                 :            : 
     301                 :            : /**
     302                 :            :  * g_datalist_clear: (skip)
     303                 :            :  * @datalist: a datalist.
     304                 :            :  *
     305                 :            :  * Frees all the data elements of the datalist.
     306                 :            :  * The data elements' destroy functions are called
     307                 :            :  * if they have been set.
     308                 :            :  **/
     309                 :            : void
     310                 :    3412313 : g_datalist_clear (GData **datalist)
     311                 :            : {
     312                 :            :   GData *data;
     313                 :            :   guint i;
     314                 :            : 
     315                 :    3412313 :   g_return_if_fail (datalist != NULL);
     316                 :            : 
     317                 :    3412313 :   data = g_datalist_lock_and_get (datalist);
     318                 :            : 
     319         [ +  + ]:    3412313 :   if (!data)
     320                 :            :     {
     321                 :         92 :       g_datalist_unlock (datalist);
     322                 :         92 :       return;
     323                 :            :     }
     324                 :            : 
     325                 :    3412221 :   g_datalist_unlock_and_set (datalist, NULL);
     326                 :            : 
     327         [ +  + ]:    6889606 :   for (i = 0; i < data->len; i++)
     328                 :            :     {
     329   [ +  -  +  + ]:    3477385 :       if (data->data[i].data && data->data[i].destroy)
     330                 :    3475621 :         data->data[i].destroy (data->data[i].data);
     331                 :            :     }
     332                 :            : 
     333                 :    3412221 :   g_free (data);
     334                 :            : }
     335                 :            : 
     336                 :            : /* HOLDS: g_dataset_global_lock */
     337                 :            : static inline GDataset*
     338                 :         32 : g_dataset_lookup (gconstpointer dataset_location)
     339                 :            : {
     340                 :            :   GDataset *dataset;
     341                 :            :   
     342   [ +  +  +  + ]:         32 :   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
     343                 :         14 :     return g_dataset_cached;
     344                 :            :   
     345                 :         18 :   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
     346         [ +  + ]:         18 :   if (dataset)
     347                 :          7 :     g_dataset_cached = dataset;
     348                 :            :   
     349                 :         18 :   return dataset;
     350                 :            : }
     351                 :            : 
     352                 :            : /* HOLDS: g_dataset_global_lock */
     353                 :            : static void
     354                 :          7 : g_dataset_destroy_internal (GDataset *dataset)
     355                 :            : {
     356                 :            :   gconstpointer dataset_location;
     357                 :            :   
     358                 :          7 :   dataset_location = dataset->location;
     359         [ +  - ]:          9 :   while (dataset)
     360                 :            :     {
     361                 :            :       GData *data;
     362                 :            :       guint i;
     363                 :            : 
     364                 :          9 :       data = G_DATALIST_GET_POINTER (&dataset->datalist);
     365                 :            : 
     366         [ +  + ]:          9 :       if (!data)
     367                 :            :         {
     368         [ +  - ]:          7 :           if (dataset == g_dataset_cached)
     369                 :          7 :             g_dataset_cached = NULL;
     370                 :          7 :           g_hash_table_remove (g_dataset_location_ht, dataset_location);
     371                 :          7 :           g_slice_free (GDataset, dataset);
     372                 :          7 :           break;
     373                 :            :         }
     374                 :            : 
     375         [ -  + ]:          2 :       G_DATALIST_SET_POINTER (&dataset->datalist, NULL);
     376                 :            : 
     377                 :          2 :       G_UNLOCK (g_dataset_global);
     378                 :            : 
     379         [ +  + ]:          8 :       for (i = 0; i < data->len; i++)
     380                 :            :         {
     381   [ +  -  +  - ]:          6 :           if (data->data[i].data && data->data[i].destroy)
     382                 :          6 :             data->data[i].destroy (data->data[i].data);
     383                 :            :         }
     384                 :          2 :       g_free (data);
     385                 :            : 
     386                 :          2 :       G_LOCK (g_dataset_global);
     387                 :          2 :       dataset = g_dataset_lookup (dataset_location);
     388                 :            :     }
     389                 :          7 : }
     390                 :            : 
     391                 :            : /**
     392                 :            :  * g_dataset_destroy:
     393                 :            :  * @dataset_location: (not nullable): the location identifying the dataset.
     394                 :            :  *
     395                 :            :  * Destroys the dataset, freeing all memory allocated, and calling any
     396                 :            :  * destroy functions set for data elements.
     397                 :            :  */
     398                 :            : void
     399                 :          2 : g_dataset_destroy (gconstpointer  dataset_location)
     400                 :            : {
     401                 :          2 :   g_return_if_fail (dataset_location != NULL);
     402                 :            :   
     403                 :          2 :   G_LOCK (g_dataset_global);
     404         [ +  - ]:          2 :   if (g_dataset_location_ht)
     405                 :            :     {
     406                 :            :       GDataset *dataset;
     407                 :            : 
     408                 :          2 :       dataset = g_dataset_lookup (dataset_location);
     409         [ +  - ]:          2 :       if (dataset)
     410                 :          2 :         g_dataset_destroy_internal (dataset);
     411                 :            :     }
     412                 :          2 :   G_UNLOCK (g_dataset_global);
     413                 :            : }
     414                 :            : 
     415                 :            : /* HOLDS: g_dataset_global_lock if dataset != null */
     416                 :            : static inline gpointer
     417                 :   24366000 : g_data_set_internal (GData        **datalist,
     418                 :            :                      GQuark         key_id,
     419                 :            :                      gpointer       new_data,
     420                 :            :                      GDestroyNotify new_destroy_func,
     421                 :            :                      GDataset      *dataset)
     422                 :            : {
     423                 :            :   GData *d;
     424                 :   24366000 :   GData *new_d = NULL;
     425                 :            :   GDataElt old, *data;
     426                 :            :   guint32 idx;
     427                 :            : 
     428                 :   24366000 :   d = g_datalist_lock_and_get (datalist);
     429                 :            : 
     430                 :   24366000 :   data = datalist_find (d, key_id, &idx);
     431                 :            : 
     432         [ +  + ]:   24366000 :   if (new_data == NULL) /* remove */
     433                 :            :     {
     434         [ +  + ]:   17347252 :       if (data)
     435                 :            :         {
     436                 :            :           GData *d_to_free;
     437                 :            : 
     438                 :    3595636 :           old = *data;
     439                 :            : 
     440                 :    3595636 :           datalist_remove (d, idx);
     441         [ +  + ]:    3595636 :           if (datalist_shrink (&d, &d_to_free))
     442                 :            :             {
     443                 :    3593030 :               g_datalist_unlock_and_set (datalist, d);
     444                 :            : 
     445                 :            :               /* the dataset destruction *must* be done
     446                 :            :                * prior to invocation of the data destroy function
     447                 :            :                */
     448   [ +  +  +  - ]:    3593030 :               if (dataset && !d)
     449                 :          5 :                 g_dataset_destroy_internal (dataset);
     450                 :            : 
     451         [ +  + ]:    3593030 :               if (d_to_free)
     452                 :    3592993 :                 g_free (d_to_free);
     453                 :            :             }
     454                 :            :           else
     455                 :       2606 :             g_datalist_unlock (datalist);
     456                 :            : 
     457                 :            :           /* We found and removed an old value
     458                 :            :            * the GData struct *must* already be unlinked
     459                 :            :            * when invoking the destroy function.
     460                 :            :            * we use (new_data==NULL && new_destroy_func!=NULL) as
     461                 :            :            * a special hint combination to "steal"
     462                 :            :            * data without destroy notification
     463                 :            :            */
     464   [ +  +  +  + ]:    3595636 :           if (old.destroy && !new_destroy_func)
     465                 :            :             {
     466         [ +  + ]:    3593775 :               if (dataset)
     467                 :          2 :                 G_UNLOCK (g_dataset_global);
     468                 :    3593775 :               old.destroy (old.data);
     469         [ +  + ]:    3593775 :               if (dataset)
     470                 :          2 :                 G_LOCK (g_dataset_global);
     471                 :    3593775 :               old.data = NULL;
     472                 :            :             }
     473                 :            : 
     474                 :    3595636 :           return old.data;
     475                 :            :         }
     476                 :            :     }
     477                 :            :   else
     478                 :            :     {
     479         [ +  + ]:    7018748 :       if (data)
     480                 :            :         {
     481         [ +  + ]:       6248 :           if (!data->destroy)
     482                 :            :             {
     483                 :       6140 :               data->data = new_data;
     484                 :       6140 :               data->destroy = new_destroy_func;
     485                 :       6140 :               g_datalist_unlock (datalist);
     486                 :            :             }
     487                 :            :           else
     488                 :            :             {
     489                 :        108 :               old = *data;
     490                 :        108 :               data->data = new_data;
     491                 :        108 :               data->destroy = new_destroy_func;
     492                 :            : 
     493                 :        108 :               g_datalist_unlock (datalist);
     494                 :            : 
     495                 :            :               /* We found and replaced an old value
     496                 :            :                * the GData struct *must* already be unlinked
     497                 :            :                * when invoking the destroy function.
     498                 :            :                */
     499         [ -  + ]:        108 :               if (dataset)
     500                 :          0 :                 G_UNLOCK (g_dataset_global);
     501                 :        108 :               old.destroy (old.data);
     502         [ -  + ]:        108 :               if (dataset)
     503                 :          0 :                 G_LOCK (g_dataset_global);
     504                 :            :             }
     505                 :       6248 :           return NULL;
     506                 :            :         }
     507                 :            : 
     508                 :            :       /* The key was not found, insert it */
     509         [ +  + ]:    7012500 :       if (datalist_append (&d, key_id, new_data, new_destroy_func))
     510                 :    6945306 :         new_d = d;
     511                 :            :     }
     512                 :            : 
     513         [ +  + ]:   20764116 :   if (new_d)
     514                 :    6945306 :     g_datalist_unlock_and_set (datalist, new_d);
     515                 :            :   else
     516                 :   13818810 :     g_datalist_unlock (datalist);
     517                 :            : 
     518                 :   20764116 :   return NULL;
     519                 :            : 
     520                 :            : }
     521                 :            : 
     522                 :            : static inline void
     523                 :          4 : g_data_remove_internal (GData  **datalist,
     524                 :            :                         GQuark  *keys,
     525                 :            :                         gsize    n_keys)
     526                 :            : {
     527                 :            :   GData *d;
     528                 :            :   GDataElt *old;
     529                 :          4 :   GDataElt *old_to_free = NULL;
     530                 :            :   GData *d_to_free;
     531                 :            :   gsize found_keys;
     532                 :            :   gsize i_keys;
     533                 :            :   guint32 i_data;
     534                 :            : 
     535                 :          4 :   d = g_datalist_lock_and_get (datalist);
     536                 :            : 
     537         [ -  + ]:          4 :   if (!d)
     538                 :            :     {
     539                 :          0 :       g_datalist_unlock (datalist);
     540                 :          0 :       return;
     541                 :            :     }
     542                 :            : 
     543                 :            :   /* Allocate an array of GDataElt to hold copies of the elements
     544                 :            :    * that are removed from the datalist. Allow enough space for all
     545                 :            :    * the keys; if a key is not found, the corresponding element of
     546                 :            :    * old is not populated, so we initialize them all to NULL to
     547                 :            :    * detect that case.
     548                 :            :    *
     549                 :            :    * At most allocate 400 bytes on the stack. Especially since we call
     550                 :            :    * out to external code, we don't know how much stack we can use. */
     551         [ +  + ]:          4 :   if (n_keys <= 400u / sizeof (GDataElt))
     552         [ +  - ]:          2 :     old = g_newa0 (GDataElt, n_keys);
     553                 :            :   else
     554                 :            :     {
     555                 :          2 :       old_to_free = g_new0 (GDataElt, n_keys);
     556                 :          2 :       old = old_to_free;
     557                 :            :     }
     558                 :            : 
     559                 :          4 :   i_data = 0;
     560                 :          4 :   found_keys = 0;
     561   [ +  +  +  + ]:       1980 :   while (i_data < d->len && found_keys < n_keys)
     562                 :            :     {
     563                 :       1976 :       GDataElt *data = &d->data[i_data];
     564                 :       1976 :       gboolean remove = FALSE;
     565                 :            : 
     566         [ +  + ]:     471216 :       for (i_keys = 0; i_keys < n_keys; i_keys++)
     567                 :            :         {
     568         [ +  + ]:     469947 :           if (data->key == keys[i_keys])
     569                 :            :             {
     570                 :            :               /* We must invoke the destroy notifications in the order of @keys.
     571                 :            :                * Hence, build up the list @old at index @i_keys. */
     572                 :        707 :               old[i_keys] = *data;
     573                 :        707 :               found_keys++;
     574                 :        707 :               remove = TRUE;
     575                 :        707 :               break;
     576                 :            :             }
     577                 :            :         }
     578                 :            : 
     579         [ +  + ]:       1976 :       if (!remove)
     580                 :            :         {
     581                 :       1269 :           i_data++;
     582                 :       1269 :           continue;
     583                 :            :         }
     584                 :            : 
     585                 :        707 :       datalist_remove (d, i_data);
     586                 :            :     }
     587                 :            : 
     588   [ +  -  +  + ]:          4 :   if (found_keys > 0 && datalist_shrink (&d, &d_to_free))
     589                 :            :     {
     590                 :          2 :       g_datalist_unlock_and_set (datalist, d);
     591         [ +  - ]:          2 :       if (d_to_free)
     592                 :          2 :         g_free (d_to_free);
     593                 :            :     }
     594                 :            :   else
     595                 :          2 :     g_datalist_unlock (datalist);
     596                 :            : 
     597         [ +  - ]:          4 :   if (found_keys > 0)
     598                 :            :     {
     599         [ +  + ]:        712 :       for (i_keys = 0; i_keys < n_keys; i_keys++)
     600                 :            :         {
     601         [ +  + ]:        708 :           if (old[i_keys].destroy)
     602                 :          3 :             old[i_keys].destroy (old[i_keys].data);
     603                 :            :         }
     604                 :            :     }
     605                 :            : 
     606         [ +  + ]:          4 :   if (G_UNLIKELY (old_to_free))
     607                 :          2 :     g_free (old_to_free);
     608                 :            : }
     609                 :            : 
     610                 :            : /**
     611                 :            :  * g_dataset_id_set_data_full: (skip)
     612                 :            :  * @dataset_location: (not nullable): the location identifying the dataset.
     613                 :            :  * @key_id: the #GQuark id to identify the data element.
     614                 :            :  * @data: the data element.
     615                 :            :  * @destroy_func: the function to call when the data element is
     616                 :            :  *                removed. This function will be called with the data
     617                 :            :  *                element and can be used to free any memory allocated
     618                 :            :  *                for it.
     619                 :            :  *
     620                 :            :  * Sets the data element associated with the given #GQuark id, and also
     621                 :            :  * the function to call when the data element is destroyed. Any
     622                 :            :  * previous data with the same key is removed, and its destroy function
     623                 :            :  * is called.
     624                 :            :  **/
     625                 :            : /**
     626                 :            :  * g_dataset_set_data_full: (skip)
     627                 :            :  * @l: the location identifying the dataset.
     628                 :            :  * @k: the string to identify the data element.
     629                 :            :  * @d: the data element.
     630                 :            :  * @f: the function to call when the data element is removed. This
     631                 :            :  *     function will be called with the data element and can be used to
     632                 :            :  *     free any memory allocated for it.
     633                 :            :  *
     634                 :            :  * Sets the data corresponding to the given string identifier, and the
     635                 :            :  * function to call when the data element is destroyed.
     636                 :            :  **/
     637                 :            : /**
     638                 :            :  * g_dataset_id_set_data:
     639                 :            :  * @l: the location identifying the dataset.
     640                 :            :  * @k: the #GQuark id to identify the data element.
     641                 :            :  * @d: the data element.
     642                 :            :  *
     643                 :            :  * Sets the data element associated with the given #GQuark id. Any
     644                 :            :  * previous data with the same key is removed, and its destroy function
     645                 :            :  * is called.
     646                 :            :  **/
     647                 :            : /**
     648                 :            :  * g_dataset_set_data:
     649                 :            :  * @l: the location identifying the dataset.
     650                 :            :  * @k: the string to identify the data element.
     651                 :            :  * @d: the data element.
     652                 :            :  *
     653                 :            :  * Sets the data corresponding to the given string identifier.
     654                 :            :  **/
     655                 :            : /**
     656                 :            :  * g_dataset_id_remove_data:
     657                 :            :  * @l: the location identifying the dataset.
     658                 :            :  * @k: the #GQuark id identifying the data element.
     659                 :            :  *
     660                 :            :  * Removes a data element from a dataset. The data element's destroy
     661                 :            :  * function is called if it has been set.
     662                 :            :  **/
     663                 :            : /**
     664                 :            :  * g_dataset_remove_data:
     665                 :            :  * @l: the location identifying the dataset.
     666                 :            :  * @k: the string identifying the data element.
     667                 :            :  *
     668                 :            :  * Removes a data element corresponding to a string. Its destroy
     669                 :            :  * function is called if it has been set.
     670                 :            :  **/
     671                 :            : void
     672                 :         17 : g_dataset_id_set_data_full (gconstpointer  dataset_location,
     673                 :            :                             GQuark         key_id,
     674                 :            :                             gpointer       data,
     675                 :            :                             GDestroyNotify destroy_func)
     676                 :            : {
     677                 :            :   GDataset *dataset;
     678                 :            :   
     679                 :         17 :   g_return_if_fail (dataset_location != NULL);
     680         [ +  + ]:         17 :   if (!data)
     681                 :          4 :     g_return_if_fail (destroy_func == NULL);
     682         [ -  + ]:         17 :   if (!key_id)
     683                 :            :     {
     684         [ #  # ]:          0 :       if (data)
     685                 :          0 :         g_return_if_fail (key_id > 0);
     686                 :            :       else
     687                 :          0 :         return;
     688                 :            :     }
     689                 :            :   
     690                 :         17 :   G_LOCK (g_dataset_global);
     691         [ +  + ]:         17 :   if (!g_dataset_location_ht)
     692                 :          1 :     g_data_initialize ();
     693                 :            :  
     694                 :         17 :   dataset = g_dataset_lookup (dataset_location);
     695         [ +  + ]:         17 :   if (!dataset)
     696                 :            :     {
     697                 :          7 :       dataset = g_slice_new (GDataset);
     698                 :          7 :       dataset->location = dataset_location;
     699                 :          7 :       g_datalist_init (&dataset->datalist);
     700                 :          7 :       g_hash_table_insert (g_dataset_location_ht, 
     701                 :          7 :                            (gpointer) dataset->location,
     702                 :            :                            dataset);
     703                 :            :     }
     704                 :            :   
     705                 :         17 :   g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
     706                 :         17 :   G_UNLOCK (g_dataset_global);
     707                 :            : }
     708                 :            : 
     709                 :            : /**
     710                 :            :  * g_datalist_id_set_data_full: (skip)
     711                 :            :  * @datalist: a datalist.
     712                 :            :  * @key_id: the #GQuark to identify the data element.
     713                 :            :  * @data: (nullable): the data element or %NULL to remove any previous element
     714                 :            :  *        corresponding to @key_id.
     715                 :            :  * @destroy_func: (nullable): the function to call when the data element is
     716                 :            :  *                removed. This function will be called with the data
     717                 :            :  *                element and can be used to free any memory allocated
     718                 :            :  *                for it. If @data is %NULL, then @destroy_func must
     719                 :            :  *                also be %NULL.
     720                 :            :  *
     721                 :            :  * Sets the data corresponding to the given #GQuark id, and the
     722                 :            :  * function to be called when the element is removed from the datalist.
     723                 :            :  * Any previous data with the same key is removed, and its destroy
     724                 :            :  * function is called.
     725                 :            :  **/
     726                 :            : /**
     727                 :            :  * g_datalist_set_data_full: (skip)
     728                 :            :  * @dl: a datalist.
     729                 :            :  * @k: the string to identify the data element.
     730                 :            :  * @d: (nullable): the data element, or %NULL to remove any previous element
     731                 :            :  *     corresponding to @k.
     732                 :            :  * @f: (nullable): the function to call when the data element is removed.
     733                 :            :  *     This function will be called with the data element and can be used to
     734                 :            :  *     free any memory allocated for it. If @d is %NULL, then @f must
     735                 :            :  *     also be %NULL.
     736                 :            :  *
     737                 :            :  * Sets the data element corresponding to the given string identifier,
     738                 :            :  * and the function to be called when the data element is removed.
     739                 :            :  **/
     740                 :            : /**
     741                 :            :  * g_datalist_id_set_data:
     742                 :            :  * @dl: a datalist.
     743                 :            :  * @q: the #GQuark to identify the data element.
     744                 :            :  * @d: (nullable): the data element, or %NULL to remove any previous element
     745                 :            :  *     corresponding to @q.
     746                 :            :  *
     747                 :            :  * Sets the data corresponding to the given #GQuark id. Any previous
     748                 :            :  * data with the same key is removed, and its destroy function is
     749                 :            :  * called.
     750                 :            :  **/
     751                 :            : /**
     752                 :            :  * g_datalist_set_data:
     753                 :            :  * @dl: a datalist.
     754                 :            :  * @k: the string to identify the data element.
     755                 :            :  * @d: (nullable): the data element, or %NULL to remove any previous element
     756                 :            :  *     corresponding to @k.
     757                 :            :  *
     758                 :            :  * Sets the data element corresponding to the given string identifier.
     759                 :            :  **/
     760                 :            : /**
     761                 :            :  * g_datalist_id_remove_data:
     762                 :            :  * @dl: a datalist.
     763                 :            :  * @q: the #GQuark identifying the data element.
     764                 :            :  *
     765                 :            :  * Removes an element, using its #GQuark identifier.
     766                 :            :  **/
     767                 :            : /**
     768                 :            :  * g_datalist_remove_data:
     769                 :            :  * @dl: a datalist.
     770                 :            :  * @k: the string identifying the data element.
     771                 :            :  *
     772                 :            :  * Removes an element using its string identifier. The data element's
     773                 :            :  * destroy function is called if it has been set.
     774                 :            :  **/
     775                 :            : void
     776                 :   24265242 : g_datalist_id_set_data_full (GData        **datalist,
     777                 :            :                              GQuark         key_id,
     778                 :            :                              gpointer       data,
     779                 :            :                              GDestroyNotify destroy_func)
     780                 :            : {
     781                 :   24265242 :   g_return_if_fail (datalist != NULL);
     782         [ +  + ]:   24265242 :   if (!data)
     783                 :   17246507 :     g_return_if_fail (destroy_func == NULL);
     784         [ +  + ]:   24265242 :   if (!key_id)
     785                 :            :     {
     786         [ -  + ]:         67 :       if (data)
     787                 :          0 :         g_return_if_fail (key_id > 0);
     788                 :            :       else
     789                 :         67 :         return;
     790                 :            :     }
     791                 :            : 
     792                 :   24265175 :   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
     793                 :            : }
     794                 :            : 
     795                 :            : /**
     796                 :            :  * g_datalist_id_remove_multiple:
     797                 :            :  * @datalist: a datalist
     798                 :            :  * @keys: (array length=n_keys): keys to remove
     799                 :            :  * @n_keys: length of @keys.
     800                 :            :  *
     801                 :            :  * Removes multiple keys from a datalist.
     802                 :            :  *
     803                 :            :  * This is more efficient than calling g_datalist_id_remove_data()
     804                 :            :  * multiple times in a row.
     805                 :            :  *
     806                 :            :  * Before 2.80, @n_keys had to be not larger than 16. Now it can be larger, but
     807                 :            :  * note that GData does a linear search, so an excessive number of keys will
     808                 :            :  * perform badly.
     809                 :            :  *
     810                 :            :  * Since: 2.74
     811                 :            :  */
     812                 :            : void
     813                 :          4 : g_datalist_id_remove_multiple (GData  **datalist,
     814                 :            :                                GQuark  *keys,
     815                 :            :                                gsize    n_keys)
     816                 :            : {
     817                 :          4 :   g_data_remove_internal (datalist, keys, n_keys);
     818                 :          4 : }
     819                 :            : 
     820                 :            : /**
     821                 :            :  * g_dataset_id_remove_no_notify: (skip)
     822                 :            :  * @dataset_location: (not nullable): the location identifying the dataset.
     823                 :            :  * @key_id: the #GQuark ID identifying the data element.
     824                 :            :  *
     825                 :            :  * Removes an element, without calling its destroy notification
     826                 :            :  * function.
     827                 :            :  *
     828                 :            :  * Returns: (nullable): the data previously stored at @key_id,
     829                 :            :  *          or %NULL if none.
     830                 :            :  **/
     831                 :            : /**
     832                 :            :  * g_dataset_remove_no_notify: (skip)
     833                 :            :  * @l: the location identifying the dataset.
     834                 :            :  * @k: the string identifying the data element.
     835                 :            :  *
     836                 :            :  * Removes an element, without calling its destroy notifier.
     837                 :            :  **/
     838                 :            : gpointer
     839                 :          1 : g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
     840                 :            :                                GQuark         key_id)
     841                 :            : {
     842                 :          1 :   gpointer ret_data = NULL;
     843                 :            : 
     844                 :          1 :   g_return_val_if_fail (dataset_location != NULL, NULL);
     845                 :            :   
     846                 :          1 :   G_LOCK (g_dataset_global);
     847   [ +  -  +  - ]:          1 :   if (key_id && g_dataset_location_ht)
     848                 :            :     {
     849                 :            :       GDataset *dataset;
     850                 :            :   
     851                 :          1 :       dataset = g_dataset_lookup (dataset_location);
     852         [ +  - ]:          1 :       if (dataset)
     853                 :          1 :         ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
     854                 :            :     } 
     855                 :          1 :   G_UNLOCK (g_dataset_global);
     856                 :            : 
     857                 :          1 :   return ret_data;
     858                 :            : }
     859                 :            : 
     860                 :            : /**
     861                 :            :  * g_datalist_id_remove_no_notify: (skip)
     862                 :            :  * @datalist: a datalist.
     863                 :            :  * @key_id: the #GQuark identifying a data element.
     864                 :            :  *
     865                 :            :  * Removes an element, without calling its destroy notification
     866                 :            :  * function.
     867                 :            :  *
     868                 :            :  * Returns: (nullable): the data previously stored at @key_id,
     869                 :            :  *          or %NULL if none.
     870                 :            :  **/
     871                 :            : /**
     872                 :            :  * g_datalist_remove_no_notify: (skip)
     873                 :            :  * @dl: a datalist.
     874                 :            :  * @k: the string identifying the data element.
     875                 :            :  *
     876                 :            :  * Removes an element, without calling its destroy notifier.
     877                 :            :  **/
     878                 :            : gpointer
     879                 :     100807 : g_datalist_id_remove_no_notify (GData   **datalist,
     880                 :            :                                 GQuark    key_id)
     881                 :            : {
     882                 :     100807 :   gpointer ret_data = NULL;
     883                 :            : 
     884                 :     100807 :   g_return_val_if_fail (datalist != NULL, NULL);
     885                 :            : 
     886         [ +  - ]:     100807 :   if (key_id)
     887                 :     100807 :     ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
     888                 :            : 
     889                 :     100807 :   return ret_data;
     890                 :            : }
     891                 :            : 
     892                 :            : /*< private >
     893                 :            :  * g_datalist_id_update_atomic:
     894                 :            :  * @datalist: the data list
     895                 :            :  * @key_id: the key to add.
     896                 :            :  * @callback: (scope call): callback to update (set, remove, steal, update) the
     897                 :            :  *   data.
     898                 :            :  * @user_data: the user data for @callback.
     899                 :            :  *
     900                 :            :  * Will call @callback while holding the lock on @datalist. Be careful to not
     901                 :            :  * end up calling into another data-list function, because the lock is not
     902                 :            :  * reentrant and deadlock will happen.
     903                 :            :  *
     904                 :            :  * The callback receives the current data and destroy function. If @key_id is
     905                 :            :  * currently not in @datalist, they will be %NULL. The callback can update
     906                 :            :  * those pointers, and @datalist will be updated with the result. Note that if
     907                 :            :  * callback modifies a received data, then it MUST steal it and take ownership
     908                 :            :  * on it. Possibly by freeing it with the provided destroy function.
     909                 :            :  *
     910                 :            :  * The point is to atomically access the entry, while holding a lock
     911                 :            :  * of @datalist. Without this, the user would have to hold their own mutex
     912                 :            :  * while handling @key_id entry.
     913                 :            :  *
     914                 :            :  * The return value of @callback is not used, except it becomes the return
     915                 :            :  * value of the function. This is an alternative to returning a result via
     916                 :            :  * @user_data.
     917                 :            :  *
     918                 :            :  * Returns: the value returned by @callback.
     919                 :            :  *
     920                 :            :  * Since: 2.80
     921                 :            :  */
     922                 :            : gpointer
     923                 :     775251 : g_datalist_id_update_atomic (GData **datalist,
     924                 :            :                              GQuark key_id,
     925                 :            :                              GDataListUpdateAtomicFunc callback,
     926                 :            :                              gpointer user_data)
     927                 :            : {
     928                 :            :   GData *d;
     929                 :            :   GDataElt *data;
     930                 :            :   gpointer new_data;
     931                 :            :   gpointer result;
     932                 :            :   GDestroyNotify new_destroy;
     933                 :            :   guint32 idx;
     934                 :     775251 :   gboolean to_unlock = TRUE;
     935                 :            : 
     936                 :     775251 :   d = g_datalist_lock_and_get (datalist);
     937                 :            : 
     938                 :     775251 :   data = datalist_find (d, key_id, &idx);
     939                 :            : 
     940         [ +  + ]:     775251 :   if (data)
     941                 :            :     {
     942                 :     713673 :       new_data = data->data;
     943                 :     713673 :       new_destroy = data->destroy;
     944                 :            :     }
     945                 :            :   else
     946                 :            :     {
     947                 :      61578 :       new_data = NULL;
     948                 :      61578 :       new_destroy = NULL;
     949                 :            :     }
     950                 :            : 
     951                 :     775251 :   result = callback (key_id, &new_data, &new_destroy, user_data);
     952                 :            : 
     953   [ +  +  +  + ]:     775251 :   if (data && !new_data)
     954                 :          1 :     {
     955                 :            :       GData *d_to_free;
     956                 :            : 
     957                 :            :       /* Remove. The callback indicates to drop the entry.
     958                 :            :        *
     959                 :            :        * The old data->data was stolen by callback(). */
     960                 :          1 :       datalist_remove (d, idx);
     961         [ +  - ]:          1 :       if (datalist_shrink (&d, &d_to_free))
     962                 :            :         {
     963                 :          1 :           g_datalist_unlock_and_set (datalist, d);
     964         [ +  - ]:          1 :           if (d_to_free)
     965                 :          1 :             g_free (d_to_free);
     966                 :          1 :           to_unlock = FALSE;
     967                 :            :         }
     968                 :            :     }
     969         [ +  + ]:     775250 :   else if (data)
     970                 :            :     {
     971                 :            :       /* Update. The callback may have provided new pointers to an existing
     972                 :            :        * entry.
     973                 :            :        *
     974                 :            :        * The old data was stolen by callback(). We only update the pointers and
     975                 :            :        * are done. */
     976                 :     713672 :       data->data = new_data;
     977                 :     713672 :       data->destroy = new_destroy;
     978                 :            :     }
     979   [ +  -  -  + ]:      61578 :   else if (!data && !new_data)
     980                 :            :     {
     981                 :            :       /* Absent. No change. The entry didn't exist and still does not. */
     982                 :            :     }
     983                 :            :   else
     984                 :            :     {
     985                 :            :       /* Add. Add a new entry that didn't exist previously. */
     986         [ +  + ]:      61578 :       if (datalist_append (&d, key_id, new_data, new_destroy))
     987                 :            :         {
     988                 :      61552 :           g_datalist_unlock_and_set (datalist, d);
     989                 :      61552 :           to_unlock = FALSE;
     990                 :            :         }
     991                 :            :     }
     992                 :            : 
     993         [ +  + ]:     775251 :   if (to_unlock)
     994                 :     713698 :     g_datalist_unlock (datalist);
     995                 :            : 
     996                 :     775251 :   return result;
     997                 :            : }
     998                 :            : 
     999                 :            : /**
    1000                 :            :  * g_dataset_id_get_data:
    1001                 :            :  * @dataset_location: (not nullable): the location identifying the dataset.
    1002                 :            :  * @key_id: the #GQuark id to identify the data element.
    1003                 :            :  *
    1004                 :            :  * Gets the data element corresponding to a #GQuark.
    1005                 :            :  *
    1006                 :            :  * Returns: (transfer none) (nullable): the data element corresponding to
    1007                 :            :  *          the #GQuark, or %NULL if it is not found.
    1008                 :            :  **/
    1009                 :            : /**
    1010                 :            :  * g_dataset_get_data:
    1011                 :            :  * @l: the location identifying the dataset.
    1012                 :            :  * @k: the string identifying the data element.
    1013                 :            :  *
    1014                 :            :  * Gets the data element corresponding to a string.
    1015                 :            :  *
    1016                 :            :  * Returns: (transfer none) (nullable): the data element corresponding to
    1017                 :            :  *          the string, or %NULL if it is not found.
    1018                 :            :  **/
    1019                 :            : gpointer
    1020                 :         12 : g_dataset_id_get_data (gconstpointer  dataset_location,
    1021                 :            :                        GQuark         key_id)
    1022                 :            : {
    1023                 :         12 :   gpointer retval = NULL;
    1024                 :            : 
    1025                 :         12 :   g_return_val_if_fail (dataset_location != NULL, NULL);
    1026                 :            :   
    1027                 :         12 :   G_LOCK (g_dataset_global);
    1028   [ +  +  +  - ]:         12 :   if (key_id && g_dataset_location_ht)
    1029                 :            :     {
    1030                 :            :       GDataset *dataset;
    1031                 :            :       
    1032                 :          9 :       dataset = g_dataset_lookup (dataset_location);
    1033         [ +  + ]:          9 :       if (dataset)
    1034                 :          5 :         retval = g_datalist_id_get_data (&dataset->datalist, key_id);
    1035                 :            :     }
    1036                 :         12 :   G_UNLOCK (g_dataset_global);
    1037                 :            :  
    1038                 :         12 :   return retval;
    1039                 :            : }
    1040                 :            : 
    1041                 :            : /**
    1042                 :            :  * g_datalist_id_get_data:
    1043                 :            :  * @datalist: a datalist.
    1044                 :            :  * @key_id: the #GQuark identifying a data element.
    1045                 :            :  *
    1046                 :            :  * Retrieves the data element corresponding to @key_id.
    1047                 :            :  *
    1048                 :            :  * Returns: (transfer none) (nullable): the data element, or %NULL if
    1049                 :            :  *          it is not found.
    1050                 :            :  */
    1051                 :            : gpointer
    1052                 :   25220075 : g_datalist_id_get_data (GData  **datalist,
    1053                 :            :                         GQuark   key_id)
    1054                 :            : {
    1055                 :   25220075 :   return g_datalist_id_dup_data (datalist, key_id, NULL, NULL);
    1056                 :            : }
    1057                 :            : 
    1058                 :            : /**
    1059                 :            :  * GDuplicateFunc:
    1060                 :            :  * @data: the data to duplicate
    1061                 :            :  * @user_data: (closure): user data that was specified in
    1062                 :            :  *             g_datalist_id_dup_data()
    1063                 :            :  *
    1064                 :            :  * The type of functions that are used to 'duplicate' an object.
    1065                 :            :  * What this means depends on the context, it could just be
    1066                 :            :  * incrementing the reference count, if @data is a ref-counted
    1067                 :            :  * object.
    1068                 :            :  *
    1069                 :            :  * Returns: a duplicate of data
    1070                 :            :  */
    1071                 :            : 
    1072                 :            : /**
    1073                 :            :  * g_datalist_id_dup_data: (skip)
    1074                 :            :  * @datalist: location of a datalist
    1075                 :            :  * @key_id: the #GQuark identifying a data element
    1076                 :            :  * @dup_func: (scope call) (closure user_data) (nullable): function to
    1077                 :            :  *   duplicate the old value
    1078                 :            :  * @user_data: passed as user_data to @dup_func
    1079                 :            :  *
    1080                 :            :  * This is a variant of g_datalist_id_get_data() which
    1081                 :            :  * returns a 'duplicate' of the value. @dup_func defines the
    1082                 :            :  * meaning of 'duplicate' in this context, it could e.g.
    1083                 :            :  * take a reference on a ref-counted object.
    1084                 :            :  *
    1085                 :            :  * If the @key_id is not set in the datalist then @dup_func
    1086                 :            :  * will be called with a %NULL argument.
    1087                 :            :  *
    1088                 :            :  * Note that @dup_func is called while the datalist is locked, so it
    1089                 :            :  * is not allowed to read or modify the datalist.
    1090                 :            :  *
    1091                 :            :  * This function can be useful to avoid races when multiple
    1092                 :            :  * threads are using the same datalist and the same key.
    1093                 :            :  *
    1094                 :            :  * Returns: (nullable): the result of calling @dup_func on the value
    1095                 :            :  *     associated with @key_id in @datalist, or %NULL if not set.
    1096                 :            :  *     If @dup_func is %NULL, the value is returned unmodified.
    1097                 :            :  *
    1098                 :            :  * Since: 2.34
    1099                 :            :  */
    1100                 :            : gpointer
    1101                 :   25220078 : g_datalist_id_dup_data (GData          **datalist,
    1102                 :            :                         GQuark           key_id,
    1103                 :            :                         GDuplicateFunc   dup_func,
    1104                 :            :                         gpointer         user_data)
    1105                 :            : {
    1106                 :   25220078 :   gpointer val = NULL;
    1107                 :   25220078 :   gpointer retval = NULL;
    1108                 :            :   GData *d;
    1109                 :            :   GDataElt *data;
    1110                 :            : 
    1111                 :   25220078 :   d = g_datalist_lock_and_get (datalist);
    1112                 :            : 
    1113                 :   25220078 :   data = datalist_find (d, key_id, NULL);
    1114         [ +  + ]:   25220078 :   if (data)
    1115                 :   17123986 :     val = data->data;
    1116                 :            : 
    1117         [ +  + ]:   25220078 :   if (dup_func)
    1118                 :          3 :     retval = dup_func (val, user_data);
    1119                 :            :   else
    1120                 :   25220075 :     retval = val;
    1121                 :            : 
    1122                 :   25220078 :   g_datalist_unlock (datalist);
    1123                 :            : 
    1124                 :   25220078 :   return retval;
    1125                 :            : }
    1126                 :            : 
    1127                 :            : /**
    1128                 :            :  * g_datalist_id_replace_data: (skip)
    1129                 :            :  * @datalist: location of a datalist
    1130                 :            :  * @key_id: the #GQuark identifying a data element
    1131                 :            :  * @oldval: (nullable): the old value to compare against
    1132                 :            :  * @newval: (nullable): the new value to replace it with
    1133                 :            :  * @destroy: (nullable): destroy notify for the new value
    1134                 :            :  * @old_destroy: (out) (optional): destroy notify for the existing value
    1135                 :            :  *
    1136                 :            :  * Compares the member that is associated with @key_id in
    1137                 :            :  * @datalist to @oldval, and if they are the same, replace
    1138                 :            :  * @oldval with @newval.
    1139                 :            :  *
    1140                 :            :  * This is like a typical atomic compare-and-exchange
    1141                 :            :  * operation, for a member of @datalist.
    1142                 :            :  *
    1143                 :            :  * If the previous value was replaced then ownership of the
    1144                 :            :  * old value (@oldval) is passed to the caller, including
    1145                 :            :  * the registered destroy notify for it (passed out in @old_destroy).
    1146                 :            :  * Its up to the caller to free this as they wish, which may
    1147                 :            :  * or may not include using @old_destroy as sometimes replacement
    1148                 :            :  * should not destroy the object in the normal way.
    1149                 :            :  *
    1150                 :            :  * Returns: %TRUE if the existing value for @key_id was replaced
    1151                 :            :  *  by @newval, %FALSE otherwise.
    1152                 :            :  *
    1153                 :            :  * Since: 2.34
    1154                 :            :  */
    1155                 :            : gboolean
    1156                 :     119556 : g_datalist_id_replace_data (GData          **datalist,
    1157                 :            :                             GQuark           key_id,
    1158                 :            :                             gpointer         oldval,
    1159                 :            :                             gpointer         newval,
    1160                 :            :                             GDestroyNotify   destroy,
    1161                 :            :                             GDestroyNotify  *old_destroy)
    1162                 :            : {
    1163                 :     119556 :   gpointer val = NULL;
    1164                 :            :   GData *d;
    1165                 :            :   GDataElt *data;
    1166                 :     119556 :   GData *d_to_free = NULL;
    1167                 :     119556 :   gboolean set_d = FALSE;
    1168                 :            :   guint32 idx;
    1169                 :            : 
    1170                 :     119556 :   g_return_val_if_fail (datalist != NULL, FALSE);
    1171                 :     119556 :   g_return_val_if_fail (key_id != 0, FALSE);
    1172                 :            : 
    1173         [ +  + ]:     119556 :   if (old_destroy)
    1174                 :          5 :     *old_destroy = NULL;
    1175                 :            : 
    1176                 :     119556 :   d = g_datalist_lock_and_get (datalist);
    1177                 :            : 
    1178                 :     119556 :   data = datalist_find (d, key_id, &idx);
    1179         [ +  + ]:     119556 :   if (data)
    1180                 :            :     {
    1181                 :     119554 :       val = data->data;
    1182         [ +  + ]:     119554 :       if (val == oldval)
    1183                 :            :         {
    1184         [ +  + ]:     100002 :           if (old_destroy)
    1185                 :          2 :             *old_destroy = data->destroy;
    1186         [ +  + ]:     100002 :           if (newval != NULL)
    1187                 :            :             {
    1188                 :     100001 :               data->data = newval;
    1189                 :     100001 :               data->destroy = destroy;
    1190                 :            :             }
    1191                 :            :           else
    1192                 :            :             {
    1193                 :          1 :               datalist_remove (d, idx);
    1194         [ +  - ]:          1 :               if (datalist_shrink (&d, &d_to_free))
    1195                 :          1 :                 set_d = TRUE;
    1196                 :            :             }
    1197                 :            :         }
    1198                 :            :     }
    1199                 :            : 
    1200   [ +  +  +  -  :     119556 :   if (val == NULL && oldval == NULL && newval != NULL)
                   +  - ]
    1201                 :            :     {
    1202         [ +  - ]:          2 :       if (datalist_append (&d, key_id, newval, destroy))
    1203                 :            :         {
    1204                 :          2 :           set_d = TRUE;
    1205                 :            :         }
    1206                 :            :     }
    1207                 :            : 
    1208         [ +  + ]:     119556 :   if (set_d)
    1209                 :          3 :     g_datalist_unlock_and_set (datalist, d);
    1210                 :            :   else
    1211                 :     119553 :     g_datalist_unlock (datalist);
    1212                 :            : 
    1213         [ +  + ]:     119556 :   if (d_to_free)
    1214                 :          1 :     g_free (d_to_free);
    1215                 :            : 
    1216                 :     119556 :   return val == oldval;
    1217                 :            : }
    1218                 :            : 
    1219                 :            : /**
    1220                 :            :  * g_datalist_get_data:
    1221                 :            :  * @datalist: a datalist.
    1222                 :            :  * @key: the string identifying a data element.
    1223                 :            :  *
    1224                 :            :  * Gets a data element, using its string identifier. This is slower than
    1225                 :            :  * g_datalist_id_get_data() because it compares strings.
    1226                 :            :  *
    1227                 :            :  * Returns: (transfer none) (nullable): the data element, or %NULL if it
    1228                 :            :  *          is not found.
    1229                 :            :  **/
    1230                 :            : gpointer
    1231                 :     175504 : g_datalist_get_data (GData       **datalist,
    1232                 :            :                      const gchar *key)
    1233                 :            : {
    1234                 :     175504 :   gpointer res = NULL;
    1235                 :            :   GData *d;
    1236                 :            :   GDataElt *data, *data_end;
    1237                 :            : 
    1238                 :     175504 :   g_return_val_if_fail (datalist != NULL, NULL);
    1239                 :            : 
    1240                 :     175504 :   d = g_datalist_lock_and_get (datalist);
    1241         [ +  + ]:     175504 :   if (d)
    1242                 :            :     {
    1243                 :     175495 :       data = d->data;
    1244                 :     175495 :       data_end = data + d->len;
    1245         [ +  + ]:     178117 :       while (data < data_end)
    1246                 :            :         {
    1247                 :            :           /* Here we intentionally compare by strings, instead of calling
    1248                 :            :            * g_quark_try_string() first.
    1249                 :            :            *
    1250                 :            :            * See commit 1cceda49b60b ('Make g_datalist_get_data not look up the
    1251                 :            :            * quark').
    1252                 :            :            */
    1253         [ +  + ]:     177242 :           if (g_strcmp0 (g_quark_to_string (data->key), key) == 0)
    1254                 :            :             {
    1255                 :     174620 :               res = data->data;
    1256                 :     174620 :               break;
    1257                 :            :             }
    1258                 :       2622 :           data++;
    1259                 :            :         }
    1260                 :            :     }
    1261                 :            : 
    1262                 :     175504 :   g_datalist_unlock (datalist);
    1263                 :            : 
    1264                 :     175504 :   return res;
    1265                 :            : }
    1266                 :            : 
    1267                 :            : /**
    1268                 :            :  * GDataForeachFunc:
    1269                 :            :  * @key_id: the #GQuark id to identifying the data element.
    1270                 :            :  * @data: the data element.
    1271                 :            :  * @user_data: (closure): user data passed to g_dataset_foreach().
    1272                 :            :  *
    1273                 :            :  * Specifies the type of function passed to g_dataset_foreach(). It is
    1274                 :            :  * called with each #GQuark id and associated data element, together
    1275                 :            :  * with the @user_data parameter supplied to g_dataset_foreach().
    1276                 :            :  **/
    1277                 :            : 
    1278                 :            : /**
    1279                 :            :  * g_dataset_foreach:
    1280                 :            :  * @dataset_location: (not nullable): the location identifying the dataset.
    1281                 :            :  * @func: (scope call) (closure user_data): the function to call for each data element.
    1282                 :            :  * @user_data: user data to pass to the function.
    1283                 :            :  *
    1284                 :            :  * Calls the given function for each data element which is associated
    1285                 :            :  * with the given location. Note that this function is NOT thread-safe.
    1286                 :            :  * So unless @dataset_location can be protected from any modifications
    1287                 :            :  * during invocation of this function, it should not be called.
    1288                 :            :  *
    1289                 :            :  * @func can make changes to the dataset, but the iteration will not
    1290                 :            :  * reflect changes made during the g_dataset_foreach() call, other
    1291                 :            :  * than skipping over elements that are removed.
    1292                 :            :  **/
    1293                 :            : void
    1294                 :          1 : g_dataset_foreach (gconstpointer    dataset_location,
    1295                 :            :                    GDataForeachFunc func,
    1296                 :            :                    gpointer         user_data)
    1297                 :            : {
    1298                 :            :   GDataset *dataset;
    1299                 :            :   
    1300                 :          1 :   g_return_if_fail (dataset_location != NULL);
    1301                 :          1 :   g_return_if_fail (func != NULL);
    1302                 :            : 
    1303                 :          1 :   G_LOCK (g_dataset_global);
    1304         [ +  - ]:          1 :   if (g_dataset_location_ht)
    1305                 :            :     {
    1306                 :          1 :       dataset = g_dataset_lookup (dataset_location);
    1307                 :          1 :       G_UNLOCK (g_dataset_global);
    1308         [ +  - ]:          1 :       if (dataset)
    1309                 :          1 :         g_datalist_foreach (&dataset->datalist, func, user_data);
    1310                 :            :     }
    1311                 :            :   else
    1312                 :            :     {
    1313                 :          0 :       G_UNLOCK (g_dataset_global);
    1314                 :            :     }
    1315                 :            : }
    1316                 :            : 
    1317                 :            : /**
    1318                 :            :  * g_datalist_foreach:
    1319                 :            :  * @datalist: a datalist.
    1320                 :            :  * @func: (scope call) (closure user_data): the function to call for each data element.
    1321                 :            :  * @user_data: user data to pass to the function.
    1322                 :            :  *
    1323                 :            :  * Calls the given function for each data element of the datalist. The
    1324                 :            :  * function is called with each data element's #GQuark id and data,
    1325                 :            :  * together with the given @user_data parameter. Note that this
    1326                 :            :  * function is NOT thread-safe. So unless @datalist can be protected
    1327                 :            :  * from any modifications during invocation of this function, it should
    1328                 :            :  * not be called.
    1329                 :            :  *
    1330                 :            :  * @func can make changes to @datalist, but the iteration will not
    1331                 :            :  * reflect changes made during the g_datalist_foreach() call, other
    1332                 :            :  * than skipping over elements that are removed.
    1333                 :            :  **/
    1334                 :            : void
    1335                 :          3 : g_datalist_foreach (GData          **datalist,
    1336                 :            :                     GDataForeachFunc func,
    1337                 :            :                     gpointer         user_data)
    1338                 :            : {
    1339                 :            :   GData *d;
    1340                 :            :   guint i, j, len;
    1341                 :            :   GQuark *keys;
    1342                 :            : 
    1343                 :          3 :   g_return_if_fail (datalist != NULL);
    1344                 :          3 :   g_return_if_fail (func != NULL);
    1345                 :            : 
    1346                 :          3 :   d = G_DATALIST_GET_POINTER (datalist);
    1347         [ +  + ]:          3 :   if (d == NULL) 
    1348                 :          1 :     return;
    1349                 :            : 
    1350                 :            :   /* We make a copy of the keys so that we can handle it changing
    1351                 :            :      in the callback */
    1352                 :          2 :   len = d->len;
    1353                 :          2 :   keys = g_new (GQuark, len);
    1354         [ +  + ]:          8 :   for (i = 0; i < len; i++)
    1355                 :          6 :     keys[i] = d->data[i].key;
    1356                 :            :   
    1357         [ +  + ]:          8 :   for (i = 0; i < len; i++)
    1358                 :            :     {
    1359                 :            :       /* A previous callback might have removed a later item, so always check that
    1360                 :            :          it still exists before calling */
    1361                 :          6 :       d = G_DATALIST_GET_POINTER (datalist);
    1362                 :            :       
    1363         [ -  + ]:          6 :       if (d == NULL)
    1364                 :          0 :         break;
    1365         [ +  - ]:         12 :       for (j = 0; j < d->len; j++)
    1366                 :            :         {
    1367         [ +  + ]:         12 :           if (d->data[j].key == keys[i]) {
    1368                 :          6 :             func (d->data[i].key, d->data[i].data, user_data);
    1369                 :          6 :             break;
    1370                 :            :           }
    1371                 :            :         }
    1372                 :            :     }
    1373                 :          2 :   g_free (keys);
    1374                 :            : }
    1375                 :            : 
    1376                 :            : /**
    1377                 :            :  * g_datalist_init: (skip)
    1378                 :            :  * @datalist: a pointer to a pointer to a datalist.
    1379                 :            :  *
    1380                 :            :  * Resets the datalist to %NULL. It does not free any memory or call
    1381                 :            :  * any destroy functions.
    1382                 :            :  **/
    1383                 :            : void
    1384                 :         20 : g_datalist_init (GData **datalist)
    1385                 :            : {
    1386                 :         20 :   g_return_if_fail (datalist != NULL);
    1387                 :            : 
    1388                 :         20 :   g_atomic_pointer_set (datalist, NULL);
    1389                 :            : }
    1390                 :            : 
    1391                 :            : /**
    1392                 :            :  * g_datalist_set_flags:
    1393                 :            :  * @datalist: pointer to the location that holds a list
    1394                 :            :  * @flags: the flags to turn on. The values of the flags are
    1395                 :            :  *   restricted by %G_DATALIST_FLAGS_MASK (currently
    1396                 :            :  *   3; giving two possible boolean flags).
    1397                 :            :  *   A value for @flags that doesn't fit within the mask is
    1398                 :            :  *   an error.
    1399                 :            :  * 
    1400                 :            :  * Turns on flag values for a data list. This function is used
    1401                 :            :  * to keep a small number of boolean flags in an object with
    1402                 :            :  * a data list without using any additional space. It is
    1403                 :            :  * not generally useful except in circumstances where space
    1404                 :            :  * is very tight. (It is used in the base #GObject type, for
    1405                 :            :  * example.)
    1406                 :            :  *
    1407                 :            :  * Since: 2.8
    1408                 :            :  **/
    1409                 :            : void
    1410                 :     110849 : g_datalist_set_flags (GData **datalist,
    1411                 :            :                       guint   flags)
    1412                 :            : {
    1413                 :     110849 :   g_return_if_fail (datalist != NULL);
    1414                 :     110849 :   g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
    1415                 :            : 
    1416                 :     110849 :   g_atomic_pointer_or (datalist, (gsize)flags);
    1417                 :            : }
    1418                 :            : 
    1419                 :            : /**
    1420                 :            :  * g_datalist_unset_flags:
    1421                 :            :  * @datalist: pointer to the location that holds a list
    1422                 :            :  * @flags: the flags to turn off. The values of the flags are
    1423                 :            :  *   restricted by %G_DATALIST_FLAGS_MASK (currently
    1424                 :            :  *   3: giving two possible boolean flags).
    1425                 :            :  *   A value for @flags that doesn't fit within the mask is
    1426                 :            :  *   an error.
    1427                 :            :  * 
    1428                 :            :  * Turns off flag values for a data list. See g_datalist_unset_flags()
    1429                 :            :  *
    1430                 :            :  * Since: 2.8
    1431                 :            :  **/
    1432                 :            : void
    1433                 :     100015 : g_datalist_unset_flags (GData **datalist,
    1434                 :            :                         guint   flags)
    1435                 :            : {
    1436                 :     100015 :   g_return_if_fail (datalist != NULL);
    1437                 :     100015 :   g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
    1438                 :            : 
    1439                 :     100015 :   g_atomic_pointer_and (datalist, ~(gsize)flags);
    1440                 :            : }
    1441                 :            : 
    1442                 :            : /**
    1443                 :            :  * g_datalist_get_flags:
    1444                 :            :  * @datalist: pointer to the location that holds a list
    1445                 :            :  * 
    1446                 :            :  * Gets flags values packed in together with the datalist.
    1447                 :            :  * See g_datalist_set_flags().
    1448                 :            :  * 
    1449                 :            :  * Returns: the flags of the datalist
    1450                 :            :  *
    1451                 :            :  * Since: 2.8
    1452                 :            :  **/
    1453                 :            : guint
    1454                 :  242013447 : g_datalist_get_flags (GData **datalist)
    1455                 :            : {
    1456                 :  242013447 :   g_return_val_if_fail (datalist != NULL, 0);
    1457                 :            :   
    1458                 :  242013447 :   return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
    1459                 :            : }
    1460                 :            : 
    1461                 :            : /* HOLDS: g_dataset_global_lock */
    1462                 :            : static void
    1463                 :          1 : g_data_initialize (void)
    1464                 :            : {
    1465                 :          1 :   g_return_if_fail (g_dataset_location_ht == NULL);
    1466                 :            : 
    1467                 :          1 :   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
    1468                 :          1 :   g_dataset_cached = NULL;
    1469                 :            : }

Generated by: LCOV version 1.14