Coverage Report

Created: 2025-07-23 06:42

/src/irssi/subprojects/glib-2.74.3/glib/gdataset.c
Line
Count
Source (jump to first uncovered line)
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 "ghash.h"
44
#include "gquark.h"
45
#include "gstrfuncs.h"
46
#include "gtestutils.h"
47
#include "gthread.h"
48
#include "glib_trace.h"
49
#include "galloca.h"
50
51
/**
52
 * SECTION:datasets
53
 * @title: Datasets
54
 * @short_description: associate groups of data elements with
55
 *                     particular memory locations
56
 *
57
 * Datasets associate groups of data elements with particular memory
58
 * locations. These are useful if you need to associate data with a
59
 * structure returned from an external library. Since you cannot modify
60
 * the structure, you use its location in memory as the key into a
61
 * dataset, where you can associate any number of data elements with it.
62
 *
63
 * There are two forms of most of the dataset functions. The first form
64
 * uses strings to identify the data elements associated with a
65
 * location. The second form uses #GQuark identifiers, which are
66
 * created with a call to g_quark_from_string() or
67
 * g_quark_from_static_string(). The second form is quicker, since it
68
 * does not require looking up the string in the hash table of #GQuark
69
 * identifiers.
70
 *
71
 * There is no function to create a dataset. It is automatically
72
 * created as soon as you add elements to it.
73
 *
74
 * To add data elements to a dataset use g_dataset_id_set_data(),
75
 * g_dataset_id_set_data_full(), g_dataset_set_data() and
76
 * g_dataset_set_data_full().
77
 *
78
 * To get data elements from a dataset use g_dataset_id_get_data() and
79
 * g_dataset_get_data().
80
 *
81
 * To iterate over all data elements in a dataset use
82
 * g_dataset_foreach() (not thread-safe).
83
 *
84
 * To remove data elements from a dataset use
85
 * g_dataset_id_remove_data() and g_dataset_remove_data().
86
 *
87
 * To destroy a dataset, use g_dataset_destroy().
88
 **/
89
90
/**
91
 * SECTION:datalist
92
 * @title: Keyed Data Lists
93
 * @short_description: lists of data elements which are accessible by a
94
 *                     string or GQuark identifier
95
 *
96
 * Keyed data lists provide lists of arbitrary data elements which can
97
 * be accessed either with a string or with a #GQuark corresponding to
98
 * the string.
99
 *
100
 * The #GQuark methods are quicker, since the strings have to be
101
 * converted to #GQuarks anyway.
102
 *
103
 * Data lists are used for associating arbitrary data with #GObjects,
104
 * using g_object_set_data() and related functions.
105
 *
106
 * To create a datalist, use g_datalist_init().
107
 *
108
 * To add data elements to a datalist use g_datalist_id_set_data(),
109
 * g_datalist_id_set_data_full(), g_datalist_set_data() and
110
 * g_datalist_set_data_full().
111
 *
112
 * To get data elements from a datalist use g_datalist_id_get_data()
113
 * and g_datalist_get_data().
114
 *
115
 * To iterate over all data elements in a datalist use
116
 * g_datalist_foreach() (not thread-safe).
117
 *
118
 * To remove data elements from a datalist use
119
 * g_datalist_id_remove_data() and g_datalist_remove_data().
120
 *
121
 * To remove all data elements from a datalist, use g_datalist_clear().
122
 **/
123
124
/**
125
 * GData:
126
 *
127
 * An opaque data structure that represents a keyed data list.
128
 *
129
 * See also: [Keyed data lists][glib-Keyed-Data-Lists].
130
 **/
131
132
/**
133
 * GDestroyNotify:
134
 * @data: the data element.
135
 *
136
 * Specifies the type of function which is called when a data element
137
 * is destroyed. It is passed the pointer to the data element and
138
 * should free any memory and resources allocated for it.
139
 **/
140
141
8.64k
#define G_DATALIST_FLAGS_MASK_INTERNAL 0x7
142
143
/* datalist pointer accesses have to be carried out atomically */
144
#define G_DATALIST_GET_POINTER(datalist)            \
145
4.32k
  ((GData*) ((gsize) g_atomic_pointer_get (datalist) & ~(gsize) G_DATALIST_FLAGS_MASK_INTERNAL))
146
147
4.32k
#define G_DATALIST_SET_POINTER(datalist, pointer)       G_STMT_START {                  \
148
4.32k
  gpointer _oldv, _newv;                                                                \
149
4.32k
  do {                                                                                  \
150
4.32k
    _oldv = g_atomic_pointer_get (datalist);                                            \
151
4.32k
    _newv = (gpointer) (((gsize) _oldv & G_DATALIST_FLAGS_MASK_INTERNAL) | (gsize) pointer);     \
152
4.32k
  } while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, _oldv, _newv));   \
153
4.32k
} G_STMT_END
154
155
/* --- structures --- */
156
typedef struct {
157
  GQuark          key;
158
  gpointer        data;
159
  GDestroyNotify  destroy;
160
} GDataElt;
161
162
typedef struct _GDataset GDataset;
163
struct _GData
164
{
165
  guint32  len;     /* Number of elements */
166
  guint32  alloc;   /* Number of allocated elements */
167
  GDataElt data[1]; /* Flexible array */
168
};
169
170
struct _GDataset
171
{
172
  gconstpointer location;
173
  GData        *datalist;
174
};
175
176
177
/* --- prototypes --- */
178
static inline GDataset* g_dataset_lookup    (gconstpointer    dataset_location);
179
static inline void  g_datalist_clear_i    (GData    **datalist);
180
static void   g_dataset_destroy_internal  (GDataset  *dataset);
181
static inline gpointer  g_data_set_internal   (GData      **datalist,
182
               GQuark       key_id,
183
               gpointer         data,
184
               GDestroyNotify   destroy_func,
185
               GDataset  *dataset);
186
static void   g_data_initialize   (void);
187
188
/* Locking model:
189
 * Each standalone GDataList is protected by a bitlock in the datalist pointer,
190
 * which protects that modification of the non-flags part of the datalist pointer
191
 * and the contents of the datalist.
192
 *
193
 * For GDataSet we have a global lock g_dataset_global that protects
194
 * the global dataset hash and cache, and additionally it protects the
195
 * datalist such that we can avoid to use the bit lock in a few places
196
 * where it is easy.
197
 */
198
199
/* --- variables --- */
200
G_LOCK_DEFINE_STATIC (g_dataset_global);
201
static GHashTable   *g_dataset_location_ht = NULL;
202
static GDataset     *g_dataset_cached = NULL; /* should this be
203
             thread specific? */
204
205
/* --- functions --- */
206
207
#define DATALIST_LOCK_BIT 2
208
209
static void
210
g_datalist_lock (GData **datalist)
211
4.32k
{
212
4.32k
  g_pointer_bit_lock ((void **)datalist, DATALIST_LOCK_BIT);
213
4.32k
}
214
215
static void
216
g_datalist_unlock (GData **datalist)
217
4.32k
{
218
4.32k
  g_pointer_bit_unlock ((void **)datalist, DATALIST_LOCK_BIT);
219
4.32k
}
220
221
/* Called with the datalist lock held, or the dataset global
222
 * lock for dataset lists
223
 */
224
static void
225
g_datalist_clear_i (GData **datalist)
226
0
{
227
0
  GData *data;
228
0
  guint i;
229
230
0
  data = G_DATALIST_GET_POINTER (datalist);
231
0
  G_DATALIST_SET_POINTER (datalist, NULL);
232
233
0
  if (data)
234
0
    {
235
0
      G_UNLOCK (g_dataset_global);
236
0
      for (i = 0; i < data->len; i++)
237
0
        {
238
0
          if (data->data[i].data && data->data[i].destroy)
239
0
            data->data[i].destroy (data->data[i].data);
240
0
        }
241
0
      G_LOCK (g_dataset_global);
242
243
0
      g_free (data);
244
0
    }
245
246
0
}
247
248
/**
249
 * g_datalist_clear: (skip)
250
 * @datalist: a datalist.
251
 *
252
 * Frees all the data elements of the datalist.
253
 * The data elements' destroy functions are called
254
 * if they have been set.
255
 **/
256
void
257
g_datalist_clear (GData **datalist)
258
4.32k
{
259
4.32k
  GData *data;
260
4.32k
  guint i;
261
262
4.32k
  g_return_if_fail (datalist != NULL);
263
264
4.32k
  g_datalist_lock (datalist);
265
266
4.32k
  data = G_DATALIST_GET_POINTER (datalist);
267
4.32k
  G_DATALIST_SET_POINTER (datalist, NULL);
268
269
4.32k
  g_datalist_unlock (datalist);
270
271
4.32k
  if (data)
272
0
    {
273
0
      for (i = 0; i < data->len; i++)
274
0
        {
275
0
          if (data->data[i].data && data->data[i].destroy)
276
0
            data->data[i].destroy (data->data[i].data);
277
0
        }
278
279
0
      g_free (data);
280
0
    }
281
4.32k
}
282
283
/* HOLDS: g_dataset_global_lock */
284
static inline GDataset*
285
g_dataset_lookup (gconstpointer dataset_location)
286
0
{
287
0
  GDataset *dataset;
288
  
289
0
  if (g_dataset_cached && g_dataset_cached->location == dataset_location)
290
0
    return g_dataset_cached;
291
  
292
0
  dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
293
0
  if (dataset)
294
0
    g_dataset_cached = dataset;
295
  
296
0
  return dataset;
297
0
}
298
299
/* HOLDS: g_dataset_global_lock */
300
static void
301
g_dataset_destroy_internal (GDataset *dataset)
302
0
{
303
0
  gconstpointer dataset_location;
304
  
305
0
  dataset_location = dataset->location;
306
0
  while (dataset)
307
0
    {
308
0
      if (G_DATALIST_GET_POINTER(&dataset->datalist) == NULL)
309
0
  {
310
0
    if (dataset == g_dataset_cached)
311
0
      g_dataset_cached = NULL;
312
0
    g_hash_table_remove (g_dataset_location_ht, dataset_location);
313
0
    g_slice_free (GDataset, dataset);
314
0
    break;
315
0
  }
316
      
317
0
      g_datalist_clear_i (&dataset->datalist);
318
0
      dataset = g_dataset_lookup (dataset_location);
319
0
    }
320
0
}
321
322
/**
323
 * g_dataset_destroy:
324
 * @dataset_location: (not nullable): the location identifying the dataset.
325
 *
326
 * Destroys the dataset, freeing all memory allocated, and calling any
327
 * destroy functions set for data elements.
328
 */
329
void
330
g_dataset_destroy (gconstpointer  dataset_location)
331
0
{
332
0
  g_return_if_fail (dataset_location != NULL);
333
  
334
0
  G_LOCK (g_dataset_global);
335
0
  if (g_dataset_location_ht)
336
0
    {
337
0
      GDataset *dataset;
338
339
0
      dataset = g_dataset_lookup (dataset_location);
340
0
      if (dataset)
341
0
  g_dataset_destroy_internal (dataset);
342
0
    }
343
0
  G_UNLOCK (g_dataset_global);
344
0
}
345
346
/* HOLDS: g_dataset_global_lock if dataset != null */
347
static inline gpointer
348
g_data_set_internal (GData    **datalist,
349
         GQuark         key_id,
350
         gpointer       new_data,
351
         GDestroyNotify new_destroy_func,
352
         GDataset    *dataset)
353
0
{
354
0
  GData *d, *old_d;
355
0
  GDataElt old, *data, *data_last, *data_end;
356
357
0
  g_datalist_lock (datalist);
358
359
0
  d = G_DATALIST_GET_POINTER (datalist);
360
361
0
  if (new_data == NULL) /* remove */
362
0
    {
363
0
      if (d)
364
0
  {
365
0
    data = d->data;
366
0
    data_last = data + d->len - 1;
367
0
    while (data <= data_last)
368
0
      {
369
0
        if (data->key == key_id)
370
0
    {
371
0
      old = *data;
372
0
      if (data != data_last)
373
0
        *data = *data_last;
374
0
      d->len--;
375
376
      /* We don't bother to shrink, but if all data are now gone
377
       * we at least free the memory
378
                   */
379
0
      if (d->len == 0)
380
0
        {
381
0
          G_DATALIST_SET_POINTER (datalist, NULL);
382
0
          g_free (d);
383
          /* datalist may be situated in dataset, so must not be
384
           * unlocked after we free it
385
           */
386
0
          g_datalist_unlock (datalist);
387
388
          /* the dataset destruction *must* be done
389
           * prior to invocation of the data destroy function
390
           */
391
0
          if (dataset)
392
0
      g_dataset_destroy_internal (dataset);
393
0
        }
394
0
      else
395
0
        {
396
0
          g_datalist_unlock (datalist);
397
0
        }
398
399
      /* We found and removed an old value
400
       * the GData struct *must* already be unlinked
401
       * when invoking the destroy function.
402
       * we use (new_data==NULL && new_destroy_func!=NULL) as
403
       * a special hint combination to "steal"
404
       * data without destroy notification
405
       */
406
0
      if (old.destroy && !new_destroy_func)
407
0
        {
408
0
          if (dataset)
409
0
      G_UNLOCK (g_dataset_global);
410
0
          old.destroy (old.data);
411
0
          if (dataset)
412
0
      G_LOCK (g_dataset_global);
413
0
          old.data = NULL;
414
0
        }
415
416
0
      return old.data;
417
0
    }
418
0
        data++;
419
0
      }
420
0
  }
421
0
    }
422
0
  else
423
0
    {
424
0
      old.data = NULL;
425
0
      if (d)
426
0
  {
427
0
    data = d->data;
428
0
    data_end = data + d->len;
429
0
    while (data < data_end)
430
0
      {
431
0
        if (data->key == key_id)
432
0
    {
433
0
      if (!data->destroy)
434
0
        {
435
0
          data->data = new_data;
436
0
          data->destroy = new_destroy_func;
437
0
          g_datalist_unlock (datalist);
438
0
        }
439
0
      else
440
0
        {
441
0
          old = *data;
442
0
          data->data = new_data;
443
0
          data->destroy = new_destroy_func;
444
445
0
          g_datalist_unlock (datalist);
446
447
          /* We found and replaced an old value
448
           * the GData struct *must* already be unlinked
449
           * when invoking the destroy function.
450
           */
451
0
          if (dataset)
452
0
      G_UNLOCK (g_dataset_global);
453
0
          old.destroy (old.data);
454
0
          if (dataset)
455
0
      G_LOCK (g_dataset_global);
456
0
        }
457
0
      return NULL;
458
0
    }
459
0
        data++;
460
0
      }
461
0
  }
462
463
      /* The key was not found, insert it */
464
0
      old_d = d;
465
0
      if (d == NULL)
466
0
  {
467
0
    d = g_malloc (sizeof (GData));
468
0
    d->len = 0;
469
0
    d->alloc = 1;
470
0
  }
471
0
      else if (d->len == d->alloc)
472
0
  {
473
0
    d->alloc = d->alloc * 2;
474
0
    d = g_realloc (d, sizeof (GData) + (d->alloc - 1) * sizeof (GDataElt));
475
0
  }
476
0
      if (old_d != d)
477
0
  G_DATALIST_SET_POINTER (datalist, d);
478
479
0
      d->data[d->len].key = key_id;
480
0
      d->data[d->len].data = new_data;
481
0
      d->data[d->len].destroy = new_destroy_func;
482
0
      d->len++;
483
0
    }
484
485
0
  g_datalist_unlock (datalist);
486
487
0
  return NULL;
488
489
0
}
490
491
static inline void
492
g_data_remove_internal (GData  **datalist,
493
                        GQuark  *keys,
494
                        gsize    n_keys)
495
0
{
496
0
  GData *d;
497
498
0
  g_datalist_lock (datalist);
499
500
0
  d = G_DATALIST_GET_POINTER (datalist);
501
502
0
  if (d)
503
0
    {
504
0
      GDataElt *old, *data, *data_end;
505
0
      gsize found_keys;
506
507
      /* Allocate an array of GDataElt to hold copies of the elements
508
       * that are removed from the datalist. Allow enough space for all
509
       * the keys; if a key is not found, the corresponding element of
510
       * old is not populated, so we initialize them all to NULL to
511
       * detect that case. */
512
0
      old = g_newa0 (GDataElt, n_keys);
513
514
0
      data = d->data;
515
0
      data_end = data + d->len;
516
0
      found_keys = 0;
517
518
0
      while (data < data_end && found_keys < n_keys)
519
0
        {
520
0
          gboolean remove = FALSE;
521
522
0
          for (gsize i = 0; i < n_keys; i++)
523
0
            {
524
0
              if (data->key == keys[i])
525
0
                {
526
0
                  old[i] = *data;
527
0
                  remove = TRUE;
528
0
                  break;
529
0
                }
530
0
            }
531
532
0
          if (remove)
533
0
            {
534
0
              GDataElt *data_last = data_end - 1;
535
536
0
              found_keys++;
537
538
0
              if (data < data_last)
539
0
                *data = *data_last;
540
541
0
              data_end--;
542
0
              d->len--;
543
544
              /* We don't bother to shrink, but if all data are now gone
545
               * we at least free the memory
546
               */
547
0
              if (d->len == 0)
548
0
                {
549
0
                  G_DATALIST_SET_POINTER (datalist, NULL);
550
0
                  g_free (d);
551
0
                  break;
552
0
                }
553
0
            }
554
0
          else
555
0
            {
556
0
              data++;
557
0
            }
558
0
        }
559
560
0
      if (found_keys > 0)
561
0
        {
562
0
          g_datalist_unlock (datalist);
563
564
0
          for (gsize i = 0; i < n_keys; i++)
565
0
            {
566
              /* If keys[i] was not found, then old[i].destroy is NULL.
567
               * Call old[i].destroy() only if keys[i] was found, and
568
               * is associated with a destroy notifier: */
569
0
              if (old[i].destroy)
570
0
                old[i].destroy (old[i].data);
571
0
            }
572
573
0
          return;
574
0
        }
575
0
    }
576
577
0
  g_datalist_unlock (datalist);
578
0
}
579
580
/**
581
 * g_dataset_id_set_data_full: (skip)
582
 * @dataset_location: (not nullable): the location identifying the dataset.
583
 * @key_id: the #GQuark id to identify the data element.
584
 * @data: the data element.
585
 * @destroy_func: the function to call when the data element is
586
 *                removed. This function will be called with the data
587
 *                element and can be used to free any memory allocated
588
 *                for it.
589
 *
590
 * Sets the data element associated with the given #GQuark id, and also
591
 * the function to call when the data element is destroyed. Any
592
 * previous data with the same key is removed, and its destroy function
593
 * is called.
594
 **/
595
/**
596
 * g_dataset_set_data_full: (skip)
597
 * @l: the location identifying the dataset.
598
 * @k: the string to identify the data element.
599
 * @d: the data element.
600
 * @f: the function to call when the data element is removed. This
601
 *     function will be called with the data element and can be used to
602
 *     free any memory allocated for it.
603
 *
604
 * Sets the data corresponding to the given string identifier, and the
605
 * function to call when the data element is destroyed.
606
 **/
607
/**
608
 * g_dataset_id_set_data:
609
 * @l: the location identifying the dataset.
610
 * @k: the #GQuark id to identify the data element.
611
 * @d: the data element.
612
 *
613
 * Sets the data element associated with the given #GQuark id. Any
614
 * previous data with the same key is removed, and its destroy function
615
 * is called.
616
 **/
617
/**
618
 * g_dataset_set_data:
619
 * @l: the location identifying the dataset.
620
 * @k: the string to identify the data element.
621
 * @d: the data element.
622
 *
623
 * Sets the data corresponding to the given string identifier.
624
 **/
625
/**
626
 * g_dataset_id_remove_data:
627
 * @l: the location identifying the dataset.
628
 * @k: the #GQuark id identifying the data element.
629
 *
630
 * Removes a data element from a dataset. The data element's destroy
631
 * function is called if it has been set.
632
 **/
633
/**
634
 * g_dataset_remove_data:
635
 * @l: the location identifying the dataset.
636
 * @k: the string identifying the data element.
637
 *
638
 * Removes a data element corresponding to a string. Its destroy
639
 * function is called if it has been set.
640
 **/
641
void
642
g_dataset_id_set_data_full (gconstpointer  dataset_location,
643
          GQuark         key_id,
644
          gpointer       data,
645
          GDestroyNotify destroy_func)
646
0
{
647
0
  GDataset *dataset;
648
  
649
0
  g_return_if_fail (dataset_location != NULL);
650
0
  if (!data)
651
0
    g_return_if_fail (destroy_func == NULL);
652
0
  if (!key_id)
653
0
    {
654
0
      if (data)
655
0
  g_return_if_fail (key_id > 0);
656
0
      else
657
0
  return;
658
0
    }
659
  
660
0
  G_LOCK (g_dataset_global);
661
0
  if (!g_dataset_location_ht)
662
0
    g_data_initialize ();
663
 
664
0
  dataset = g_dataset_lookup (dataset_location);
665
0
  if (!dataset)
666
0
    {
667
0
      dataset = g_slice_new (GDataset);
668
0
      dataset->location = dataset_location;
669
0
      g_datalist_init (&dataset->datalist);
670
0
      g_hash_table_insert (g_dataset_location_ht, 
671
0
         (gpointer) dataset->location,
672
0
         dataset);
673
0
    }
674
  
675
0
  g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
676
0
  G_UNLOCK (g_dataset_global);
677
0
}
678
679
/**
680
 * g_datalist_id_set_data_full: (skip)
681
 * @datalist: a datalist.
682
 * @key_id: the #GQuark to identify the data element.
683
 * @data: (nullable): the data element or %NULL to remove any previous element
684
 *        corresponding to @key_id.
685
 * @destroy_func: (nullable): the function to call when the data element is
686
 *                removed. This function will be called with the data
687
 *                element and can be used to free any memory allocated
688
 *                for it. If @data is %NULL, then @destroy_func must
689
 *                also be %NULL.
690
 *
691
 * Sets the data corresponding to the given #GQuark id, and the
692
 * function to be called when the element is removed from the datalist.
693
 * Any previous data with the same key is removed, and its destroy
694
 * function is called.
695
 **/
696
/**
697
 * g_datalist_set_data_full: (skip)
698
 * @dl: a datalist.
699
 * @k: the string to identify the data element.
700
 * @d: (nullable): the data element, or %NULL to remove any previous element
701
 *     corresponding to @k.
702
 * @f: (nullable): the function to call when the data element is removed.
703
 *     This function will be called with the data element and can be used to
704
 *     free any memory allocated for it. If @d is %NULL, then @f must
705
 *     also be %NULL.
706
 *
707
 * Sets the data element corresponding to the given string identifier,
708
 * and the function to be called when the data element is removed.
709
 **/
710
/**
711
 * g_datalist_id_set_data:
712
 * @dl: a datalist.
713
 * @q: the #GQuark to identify the data element.
714
 * @d: (nullable): the data element, or %NULL to remove any previous element
715
 *     corresponding to @q.
716
 *
717
 * Sets the data corresponding to the given #GQuark id. Any previous
718
 * data with the same key is removed, and its destroy function is
719
 * called.
720
 **/
721
/**
722
 * g_datalist_set_data:
723
 * @dl: a datalist.
724
 * @k: the string to identify the data element.
725
 * @d: (nullable): the data element, or %NULL to remove any previous element
726
 *     corresponding to @k.
727
 *
728
 * Sets the data element corresponding to the given string identifier.
729
 **/
730
/**
731
 * g_datalist_id_remove_data:
732
 * @dl: a datalist.
733
 * @q: the #GQuark identifying the data element.
734
 *
735
 * Removes an element, using its #GQuark identifier.
736
 **/
737
/**
738
 * g_datalist_remove_data:
739
 * @dl: a datalist.
740
 * @k: the string identifying the data element.
741
 *
742
 * Removes an element using its string identifier. The data element's
743
 * destroy function is called if it has been set.
744
 **/
745
void
746
g_datalist_id_set_data_full (GData    **datalist,
747
           GQuark         key_id,
748
           gpointer       data,
749
           GDestroyNotify destroy_func)
750
0
{
751
0
  g_return_if_fail (datalist != NULL);
752
0
  if (!data)
753
0
    g_return_if_fail (destroy_func == NULL);
754
0
  if (!key_id)
755
0
    {
756
0
      if (data)
757
0
  g_return_if_fail (key_id > 0);
758
0
      else
759
0
  return;
760
0
    }
761
762
0
  g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
763
0
}
764
765
/**
766
 * g_datalist_id_remove_multiple:
767
 * @datalist: a datalist
768
 * @keys: (array length=n_keys): keys to remove
769
 * @n_keys: length of @keys, must be <= 16
770
 *
771
 * Removes multiple keys from a datalist.
772
 *
773
 * This is more efficient than calling g_datalist_id_remove_data()
774
 * multiple times in a row.
775
 *
776
 * Since: 2.74
777
 */
778
void
779
g_datalist_id_remove_multiple (GData  **datalist,
780
                               GQuark  *keys,
781
                               gsize    n_keys)
782
0
{
783
0
  g_return_if_fail (n_keys <= 16);
784
785
0
  g_data_remove_internal (datalist, keys, n_keys);
786
0
}
787
788
/**
789
 * g_dataset_id_remove_no_notify: (skip)
790
 * @dataset_location: (not nullable): the location identifying the dataset.
791
 * @key_id: the #GQuark ID identifying the data element.
792
 *
793
 * Removes an element, without calling its destroy notification
794
 * function.
795
 *
796
 * Returns: (nullable): the data previously stored at @key_id,
797
 *          or %NULL if none.
798
 **/
799
/**
800
 * g_dataset_remove_no_notify: (skip)
801
 * @l: the location identifying the dataset.
802
 * @k: the string identifying the data element.
803
 *
804
 * Removes an element, without calling its destroy notifier.
805
 **/
806
gpointer
807
g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
808
             GQuark         key_id)
809
0
{
810
0
  gpointer ret_data = NULL;
811
812
0
  g_return_val_if_fail (dataset_location != NULL, NULL);
813
  
814
0
  G_LOCK (g_dataset_global);
815
0
  if (key_id && g_dataset_location_ht)
816
0
    {
817
0
      GDataset *dataset;
818
  
819
0
      dataset = g_dataset_lookup (dataset_location);
820
0
      if (dataset)
821
0
  ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
822
0
    } 
823
0
  G_UNLOCK (g_dataset_global);
824
825
0
  return ret_data;
826
0
}
827
828
/**
829
 * g_datalist_id_remove_no_notify: (skip)
830
 * @datalist: a datalist.
831
 * @key_id: the #GQuark identifying a data element.
832
 *
833
 * Removes an element, without calling its destroy notification
834
 * function.
835
 *
836
 * Returns: (nullable): the data previously stored at @key_id,
837
 *          or %NULL if none.
838
 **/
839
/**
840
 * g_datalist_remove_no_notify: (skip)
841
 * @dl: a datalist.
842
 * @k: the string identifying the data element.
843
 *
844
 * Removes an element, without calling its destroy notifier.
845
 **/
846
gpointer
847
g_datalist_id_remove_no_notify (GData **datalist,
848
        GQuark    key_id)
849
0
{
850
0
  gpointer ret_data = NULL;
851
852
0
  g_return_val_if_fail (datalist != NULL, NULL);
853
854
0
  if (key_id)
855
0
    ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
856
857
0
  return ret_data;
858
0
}
859
860
/**
861
 * g_dataset_id_get_data:
862
 * @dataset_location: (not nullable): the location identifying the dataset.
863
 * @key_id: the #GQuark id to identify the data element.
864
 *
865
 * Gets the data element corresponding to a #GQuark.
866
 *
867
 * Returns: (transfer none) (nullable): the data element corresponding to
868
 *          the #GQuark, or %NULL if it is not found.
869
 **/
870
/**
871
 * g_dataset_get_data:
872
 * @l: the location identifying the dataset.
873
 * @k: the string identifying the data element.
874
 *
875
 * Gets the data element corresponding to a string.
876
 *
877
 * Returns: (transfer none) (nullable): the data element corresponding to
878
 *          the string, or %NULL if it is not found.
879
 **/
880
gpointer
881
g_dataset_id_get_data (gconstpointer  dataset_location,
882
           GQuark         key_id)
883
0
{
884
0
  gpointer retval = NULL;
885
886
0
  g_return_val_if_fail (dataset_location != NULL, NULL);
887
  
888
0
  G_LOCK (g_dataset_global);
889
0
  if (key_id && g_dataset_location_ht)
890
0
    {
891
0
      GDataset *dataset;
892
      
893
0
      dataset = g_dataset_lookup (dataset_location);
894
0
      if (dataset)
895
0
  retval = g_datalist_id_get_data (&dataset->datalist, key_id);
896
0
    }
897
0
  G_UNLOCK (g_dataset_global);
898
 
899
0
  return retval;
900
0
}
901
902
/**
903
 * g_datalist_id_get_data:
904
 * @datalist: a datalist.
905
 * @key_id: the #GQuark identifying a data element.
906
 *
907
 * Retrieves the data element corresponding to @key_id.
908
 *
909
 * Returns: (transfer none) (nullable): the data element, or %NULL if
910
 *          it is not found.
911
 */
912
gpointer
913
g_datalist_id_get_data (GData  **datalist,
914
      GQuark   key_id)
915
0
{
916
0
  return g_datalist_id_dup_data (datalist, key_id, NULL, NULL);
917
0
}
918
919
/**
920
 * GDuplicateFunc:
921
 * @data: the data to duplicate
922
 * @user_data: (closure): user data that was specified in
923
 *             g_datalist_id_dup_data()
924
 *
925
 * The type of functions that are used to 'duplicate' an object.
926
 * What this means depends on the context, it could just be
927
 * incrementing the reference count, if @data is a ref-counted
928
 * object.
929
 *
930
 * Returns: a duplicate of data
931
 */
932
933
/**
934
 * g_datalist_id_dup_data: (skip)
935
 * @datalist: location of a datalist
936
 * @key_id: the #GQuark identifying a data element
937
 * @dup_func: (nullable) (scope call): function to duplicate the old value
938
 * @user_data: (closure): passed as user_data to @dup_func
939
 *
940
 * This is a variant of g_datalist_id_get_data() which
941
 * returns a 'duplicate' of the value. @dup_func defines the
942
 * meaning of 'duplicate' in this context, it could e.g.
943
 * take a reference on a ref-counted object.
944
 *
945
 * If the @key_id is not set in the datalist then @dup_func
946
 * will be called with a %NULL argument.
947
 *
948
 * Note that @dup_func is called while the datalist is locked, so it
949
 * is not allowed to read or modify the datalist.
950
 *
951
 * This function can be useful to avoid races when multiple
952
 * threads are using the same datalist and the same key.
953
 *
954
 * Returns: (nullable): the result of calling @dup_func on the value
955
 *     associated with @key_id in @datalist, or %NULL if not set.
956
 *     If @dup_func is %NULL, the value is returned unmodified.
957
 *
958
 * Since: 2.34
959
 */
960
gpointer
961
g_datalist_id_dup_data (GData          **datalist,
962
                        GQuark           key_id,
963
                        GDuplicateFunc   dup_func,
964
                        gpointer         user_data)
965
0
{
966
0
  gpointer val = NULL;
967
0
  gpointer retval = NULL;
968
0
  GData *d;
969
0
  GDataElt *data, *data_end;
970
971
0
  g_datalist_lock (datalist);
972
973
0
  d = G_DATALIST_GET_POINTER (datalist);
974
0
  if (d)
975
0
    {
976
0
      data = d->data;
977
0
      data_end = data + d->len;
978
0
      do
979
0
        {
980
0
          if (data->key == key_id)
981
0
            {
982
0
              val = data->data;
983
0
              break;
984
0
            }
985
0
          data++;
986
0
        }
987
0
      while (data < data_end);
988
0
    }
989
990
0
  if (dup_func)
991
0
    retval = dup_func (val, user_data);
992
0
  else
993
0
    retval = val;
994
995
0
  g_datalist_unlock (datalist);
996
997
0
  return retval;
998
0
}
999
1000
/**
1001
 * g_datalist_id_replace_data: (skip)
1002
 * @datalist: location of a datalist
1003
 * @key_id: the #GQuark identifying a data element
1004
 * @oldval: (nullable): the old value to compare against
1005
 * @newval: (nullable): the new value to replace it with
1006
 * @destroy: (nullable): destroy notify for the new value
1007
 * @old_destroy: (out) (optional): destroy notify for the existing value
1008
 *
1009
 * Compares the member that is associated with @key_id in
1010
 * @datalist to @oldval, and if they are the same, replace
1011
 * @oldval with @newval.
1012
 *
1013
 * This is like a typical atomic compare-and-exchange
1014
 * operation, for a member of @datalist.
1015
 *
1016
 * If the previous value was replaced then ownership of the
1017
 * old value (@oldval) is passed to the caller, including
1018
 * the registered destroy notify for it (passed out in @old_destroy).
1019
 * Its up to the caller to free this as they wish, which may
1020
 * or may not include using @old_destroy as sometimes replacement
1021
 * should not destroy the object in the normal way.
1022
 *
1023
 * Returns: %TRUE if the existing value for @key_id was replaced
1024
 *  by @newval, %FALSE otherwise.
1025
 *
1026
 * Since: 2.34
1027
 */
1028
gboolean
1029
g_datalist_id_replace_data (GData          **datalist,
1030
                            GQuark           key_id,
1031
                            gpointer         oldval,
1032
                            gpointer         newval,
1033
                            GDestroyNotify   destroy,
1034
                            GDestroyNotify  *old_destroy)
1035
0
{
1036
0
  gpointer val = NULL;
1037
0
  GData *d;
1038
0
  GDataElt *data, *data_end;
1039
1040
0
  g_return_val_if_fail (datalist != NULL, FALSE);
1041
0
  g_return_val_if_fail (key_id != 0, FALSE);
1042
1043
0
  if (old_destroy)
1044
0
    *old_destroy = NULL;
1045
1046
0
  g_datalist_lock (datalist);
1047
1048
0
  d = G_DATALIST_GET_POINTER (datalist);
1049
0
  if (d)
1050
0
    {
1051
0
      data = d->data;
1052
0
      data_end = data + d->len - 1;
1053
0
      while (data <= data_end)
1054
0
        {
1055
0
          if (data->key == key_id)
1056
0
            {
1057
0
              val = data->data;
1058
0
              if (val == oldval)
1059
0
                {
1060
0
                  if (old_destroy)
1061
0
                    *old_destroy = data->destroy;
1062
0
                  if (newval != NULL)
1063
0
                    {
1064
0
                      data->data = newval;
1065
0
                      data->destroy = destroy;
1066
0
                    }
1067
0
                  else
1068
0
                   {
1069
0
                     if (data != data_end)
1070
0
                       *data = *data_end;
1071
0
                     d->len--;
1072
1073
                     /* We don't bother to shrink, but if all data are now gone
1074
                      * we at least free the memory
1075
                      */
1076
0
                     if (d->len == 0)
1077
0
                       {
1078
0
                         G_DATALIST_SET_POINTER (datalist, NULL);
1079
0
                         g_free (d);
1080
0
                       }
1081
0
                   }
1082
0
                }
1083
0
              break;
1084
0
            }
1085
0
          data++;
1086
0
        }
1087
0
    }
1088
1089
0
  if (val == NULL && oldval == NULL && newval != NULL)
1090
0
    {
1091
0
      GData *old_d;
1092
1093
      /* insert newval */
1094
0
      old_d = d;
1095
0
      if (d == NULL)
1096
0
  {
1097
0
          d = g_malloc (sizeof (GData));
1098
0
          d->len = 0;
1099
0
          d->alloc = 1;
1100
0
        }
1101
0
      else if (d->len == d->alloc)
1102
0
        {
1103
0
          d->alloc = d->alloc * 2;
1104
0
          d = g_realloc (d, sizeof (GData) + (d->alloc - 1) * sizeof (GDataElt));
1105
0
        }
1106
0
      if (old_d != d)
1107
0
        G_DATALIST_SET_POINTER (datalist, d);
1108
1109
0
      d->data[d->len].key = key_id;
1110
0
      d->data[d->len].data = newval;
1111
0
      d->data[d->len].destroy = destroy;
1112
0
      d->len++;
1113
0
    }
1114
1115
0
  g_datalist_unlock (datalist);
1116
1117
0
  return val == oldval;
1118
0
}
1119
1120
/**
1121
 * g_datalist_get_data:
1122
 * @datalist: a datalist.
1123
 * @key: the string identifying a data element.
1124
 *
1125
 * Gets a data element, using its string identifier. This is slower than
1126
 * g_datalist_id_get_data() because it compares strings.
1127
 *
1128
 * Returns: (transfer none) (nullable): the data element, or %NULL if it
1129
 *          is not found.
1130
 **/
1131
gpointer
1132
g_datalist_get_data (GData   **datalist,
1133
         const gchar *key)
1134
0
{
1135
0
  gpointer res = NULL;
1136
0
  GData *d;
1137
0
  GDataElt *data, *data_end;
1138
1139
0
  g_return_val_if_fail (datalist != NULL, NULL);
1140
1141
0
  g_datalist_lock (datalist);
1142
1143
0
  d = G_DATALIST_GET_POINTER (datalist);
1144
0
  if (d)
1145
0
    {
1146
0
      data = d->data;
1147
0
      data_end = data + d->len;
1148
0
      while (data < data_end)
1149
0
  {
1150
0
    if (g_strcmp0 (g_quark_to_string (data->key), key) == 0)
1151
0
      {
1152
0
        res = data->data;
1153
0
        break;
1154
0
      }
1155
0
    data++;
1156
0
  }
1157
0
    }
1158
1159
0
  g_datalist_unlock (datalist);
1160
1161
0
  return res;
1162
0
}
1163
1164
/**
1165
 * GDataForeachFunc:
1166
 * @key_id: the #GQuark id to identifying the data element.
1167
 * @data: the data element.
1168
 * @user_data: (closure): user data passed to g_dataset_foreach().
1169
 *
1170
 * Specifies the type of function passed to g_dataset_foreach(). It is
1171
 * called with each #GQuark id and associated data element, together
1172
 * with the @user_data parameter supplied to g_dataset_foreach().
1173
 **/
1174
1175
/**
1176
 * g_dataset_foreach:
1177
 * @dataset_location: (not nullable): the location identifying the dataset.
1178
 * @func: (scope call): the function to call for each data element.
1179
 * @user_data: (closure): user data to pass to the function.
1180
 *
1181
 * Calls the given function for each data element which is associated
1182
 * with the given location. Note that this function is NOT thread-safe.
1183
 * So unless @dataset_location can be protected from any modifications
1184
 * during invocation of this function, it should not be called.
1185
 *
1186
 * @func can make changes to the dataset, but the iteration will not
1187
 * reflect changes made during the g_dataset_foreach() call, other
1188
 * than skipping over elements that are removed.
1189
 **/
1190
void
1191
g_dataset_foreach (gconstpointer    dataset_location,
1192
       GDataForeachFunc func,
1193
       gpointer         user_data)
1194
0
{
1195
0
  GDataset *dataset;
1196
  
1197
0
  g_return_if_fail (dataset_location != NULL);
1198
0
  g_return_if_fail (func != NULL);
1199
1200
0
  G_LOCK (g_dataset_global);
1201
0
  if (g_dataset_location_ht)
1202
0
    {
1203
0
      dataset = g_dataset_lookup (dataset_location);
1204
0
      G_UNLOCK (g_dataset_global);
1205
0
      if (dataset)
1206
0
  g_datalist_foreach (&dataset->datalist, func, user_data);
1207
0
    }
1208
0
  else
1209
0
    {
1210
0
      G_UNLOCK (g_dataset_global);
1211
0
    }
1212
0
}
1213
1214
/**
1215
 * g_datalist_foreach:
1216
 * @datalist: a datalist.
1217
 * @func: (scope call): the function to call for each data element.
1218
 * @user_data: (closure): user data to pass to the function.
1219
 *
1220
 * Calls the given function for each data element of the datalist. The
1221
 * function is called with each data element's #GQuark id and data,
1222
 * together with the given @user_data parameter. Note that this
1223
 * function is NOT thread-safe. So unless @datalist can be protected
1224
 * from any modifications during invocation of this function, it should
1225
 * not be called.
1226
 *
1227
 * @func can make changes to @datalist, but the iteration will not
1228
 * reflect changes made during the g_datalist_foreach() call, other
1229
 * than skipping over elements that are removed.
1230
 **/
1231
void
1232
g_datalist_foreach (GData    **datalist,
1233
        GDataForeachFunc func,
1234
        gpointer         user_data)
1235
0
{
1236
0
  GData *d;
1237
0
  guint i, j, len;
1238
0
  GQuark *keys;
1239
1240
0
  g_return_if_fail (datalist != NULL);
1241
0
  g_return_if_fail (func != NULL);
1242
1243
0
  d = G_DATALIST_GET_POINTER (datalist);
1244
0
  if (d == NULL) 
1245
0
    return;
1246
1247
  /* We make a copy of the keys so that we can handle it changing
1248
     in the callback */
1249
0
  len = d->len;
1250
0
  keys = g_new (GQuark, len);
1251
0
  for (i = 0; i < len; i++)
1252
0
    keys[i] = d->data[i].key;
1253
  
1254
0
  for (i = 0; i < len; i++)
1255
0
    {
1256
      /* A previous callback might have removed a later item, so always check that
1257
   it still exists before calling */
1258
0
      d = G_DATALIST_GET_POINTER (datalist);
1259
      
1260
0
      if (d == NULL)
1261
0
  break;
1262
0
      for (j = 0; j < d->len; j++)
1263
0
  {
1264
0
    if (d->data[j].key == keys[i]) {
1265
0
      func (d->data[i].key, d->data[i].data, user_data);
1266
0
      break;
1267
0
    }
1268
0
  }
1269
0
    }
1270
0
  g_free (keys);
1271
0
}
1272
1273
/**
1274
 * g_datalist_init: (skip)
1275
 * @datalist: a pointer to a pointer to a datalist.
1276
 *
1277
 * Resets the datalist to %NULL. It does not free any memory or call
1278
 * any destroy functions.
1279
 **/
1280
void
1281
g_datalist_init (GData **datalist)
1282
4.32k
{
1283
4.32k
  g_return_if_fail (datalist != NULL);
1284
1285
4.32k
  g_atomic_pointer_set (datalist, NULL);
1286
4.32k
}
1287
1288
/**
1289
 * g_datalist_set_flags:
1290
 * @datalist: pointer to the location that holds a list
1291
 * @flags: the flags to turn on. The values of the flags are
1292
 *   restricted by %G_DATALIST_FLAGS_MASK (currently
1293
 *   3; giving two possible boolean flags).
1294
 *   A value for @flags that doesn't fit within the mask is
1295
 *   an error.
1296
 * 
1297
 * Turns on flag values for a data list. This function is used
1298
 * to keep a small number of boolean flags in an object with
1299
 * a data list without using any additional space. It is
1300
 * not generally useful except in circumstances where space
1301
 * is very tight. (It is used in the base #GObject type, for
1302
 * example.)
1303
 *
1304
 * Since: 2.8
1305
 **/
1306
void
1307
g_datalist_set_flags (GData **datalist,
1308
          guint   flags)
1309
0
{
1310
0
  g_return_if_fail (datalist != NULL);
1311
0
  g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1312
1313
0
  g_atomic_pointer_or (datalist, (gsize)flags);
1314
0
}
1315
1316
/**
1317
 * g_datalist_unset_flags:
1318
 * @datalist: pointer to the location that holds a list
1319
 * @flags: the flags to turn off. The values of the flags are
1320
 *   restricted by %G_DATALIST_FLAGS_MASK (currently
1321
 *   3: giving two possible boolean flags).
1322
 *   A value for @flags that doesn't fit within the mask is
1323
 *   an error.
1324
 * 
1325
 * Turns off flag values for a data list. See g_datalist_unset_flags()
1326
 *
1327
 * Since: 2.8
1328
 **/
1329
void
1330
g_datalist_unset_flags (GData **datalist,
1331
      guint   flags)
1332
0
{
1333
0
  g_return_if_fail (datalist != NULL);
1334
0
  g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1335
1336
0
  g_atomic_pointer_and (datalist, ~(gsize)flags);
1337
0
}
1338
1339
/**
1340
 * g_datalist_get_flags:
1341
 * @datalist: pointer to the location that holds a list
1342
 * 
1343
 * Gets flags values packed in together with the datalist.
1344
 * See g_datalist_set_flags().
1345
 * 
1346
 * Returns: the flags of the datalist
1347
 *
1348
 * Since: 2.8
1349
 **/
1350
guint
1351
g_datalist_get_flags (GData **datalist)
1352
0
{
1353
0
  g_return_val_if_fail (datalist != NULL, 0);
1354
  
1355
0
  return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
1356
0
}
1357
1358
/* HOLDS: g_dataset_global_lock */
1359
static void
1360
g_data_initialize (void)
1361
0
{
1362
0
  g_return_if_fail (g_dataset_location_ht == NULL);
1363
1364
0
  g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
1365
0
  g_dataset_cached = NULL;
1366
0
}