Coverage Report

Created: 2026-01-09 07:21

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