Coverage Report

Created: 2026-05-14 07:07

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
 * 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
804k
#define G_DATALIST_FLAGS_MASK_INTERNAL 0x7
70
71
/* When GData.alloc grows to size ALLOC_THRESHOLD_INDEX, we reserve one additional
72
 * GHashTable* at &data->data[data->alloc]. This will contain the index for fast
73
 * lookup. See datalist_index*() helpers.
74
 *
75
 * Note that we grow the GData.data buffer by doubling the allocation size. So
76
 * we first allocate 64 entries, when adding the 33 entry.
77
 *
78
 * Conversely, we exponentially shrink the buffer. That means, when we remove
79
 * entries and reach 16 (or lower), we will shrink the buffer from 64 to 32
80
 * entries (and stop using the index).
81
 *
82
 * So we start using the index when adding >= 33 entries. And stop using it
83
 * when removing to <= 16 entries.
84
 */
85
108k
#define ALLOC_THRESHOLD_INDEX 64u
86
87
#define G_DATALIST_CLEAN_POINTER(ptr) \
88
804k
  ((GData *) ((gpointer) (((guintptr) (ptr)) & ~((guintptr) G_DATALIST_FLAGS_MASK_INTERNAL))))
89
90
/* datalist pointer accesses have to be carried out atomically */
91
#define G_DATALIST_GET_POINTER(datalist) \
92
0
  G_DATALIST_CLEAN_POINTER (g_atomic_pointer_get (datalist))
93
94
0
#define G_DATALIST_SET_POINTER(datalist, pointer)       G_STMT_START {                           \
95
0
  gpointer _oldv = g_atomic_pointer_get (datalist);                                              \
96
0
  gpointer _newv;                                                                                \
97
0
  do {                                                                                           \
98
0
    _newv = (gpointer) (((guintptr) _oldv & ((guintptr) G_DATALIST_FLAGS_MASK_INTERNAL)) | (guintptr) pointer); \
99
0
  } while (!g_atomic_pointer_compare_and_exchange_full ((void**) datalist, _oldv,                \
100
0
                                                        _newv, &_oldv));                         \
101
0
} G_STMT_END
102
103
/* --- structures --- */
104
typedef struct {
105
  GQuark          key;
106
  gpointer        data;
107
  GDestroyNotify  destroy;
108
} GDataElt;
109
110
typedef struct _GDataset GDataset;
111
struct _GData
112
{
113
  guint32  len;     /* Number of elements */
114
  guint32  alloc;   /* Number of allocated elements */
115
  GDataElt data[1]; /* Flexible array */
116
};
117
118
struct _GDataset
119
{
120
  gconstpointer location;
121
  GData        *datalist;
122
};
123
124
125
/* --- prototypes --- */
126
static inline GDataset* g_dataset_lookup    (gconstpointer    dataset_location);
127
static void   g_dataset_destroy_internal  (GDataset  *dataset);
128
static inline gpointer  g_data_set_internal   (GData      **datalist,
129
               GQuark       key_id,
130
               gpointer         data,
131
               GDestroyNotify   destroy_func,
132
               GDataset  *dataset);
133
static void   g_data_initialize   (void);
134
135
/* Locking model:
136
 * Each standalone GDataList is protected by a bitlock in the datalist pointer,
137
 * which protects that modification of the non-flags part of the datalist pointer
138
 * and the contents of the datalist.
139
 *
140
 * For GDataSet we have a global lock g_dataset_global that protects
141
 * the global dataset hash and cache, and additionally it protects the
142
 * datalist such that we can avoid to use the bit lock in a few places
143
 * where it is easy.
144
 */
145
146
/* --- variables --- */
147
G_LOCK_DEFINE_STATIC (g_dataset_global);
148
static GHashTable   *g_dataset_location_ht = NULL;
149
static GDataset     *g_dataset_cached = NULL; /* should this be
150
             thread specific? */
151
152
/* --- functions --- */
153
154
#define DATALIST_LOCK_BIT 2
155
156
G_ALWAYS_INLINE static inline GData *
157
g_datalist_lock_and_get (GData **datalist)
158
804k
{
159
804k
  guintptr ptr;
160
161
804k
  g_pointer_bit_lock_and_get ((void **) datalist, DATALIST_LOCK_BIT, &ptr);
162
804k
  return G_DATALIST_CLEAN_POINTER (ptr);
163
804k
}
164
165
static void
166
g_datalist_unlock (GData **datalist)
167
599k
{
168
599k
  g_pointer_bit_unlock ((void **)datalist, DATALIST_LOCK_BIT);
169
599k
}
170
171
static void
172
g_datalist_unlock_and_set (GData **datalist, gpointer ptr)
173
204k
{
174
204k
  g_pointer_bit_unlock_and_set ((void **) datalist, DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL);
175
204k
}
176
177
static gsize
178
datalist_alloc_size (guint32 alloc)
179
108k
{
180
  /* GDataElt also contains pointer. It thus is suitable aligned for pointers,
181
   * and we can just append the pointer for the index at the end. */
182
108k
  return G_STRUCT_OFFSET (GData, data) +
183
108k
         (((gsize) alloc) * sizeof (GDataElt)) +
184
108k
         (G_UNLIKELY (alloc >= ALLOC_THRESHOLD_INDEX) ? sizeof (GHashTable *) : 0u);
185
108k
}
186
187
G_ALWAYS_INLINE static inline GHashTable **
188
datalist_index_get_ptr (GData *data)
189
733k
{
190
733k
  if (G_LIKELY (data->alloc < ALLOC_THRESHOLD_INDEX))
191
733k
    return NULL;
192
193
0
  return (gpointer) (&(data->data[data->alloc]));
194
733k
}
195
196
G_ALWAYS_INLINE static inline GHashTable *
197
datalist_index_get (GData *data)
198
733k
{
199
733k
  GHashTable **p_index;
200
201
733k
  p_index = datalist_index_get_ptr (data);
202
203
733k
#if G_ENABLE_DEBUG
204
733k
  g_assert (!p_index || *p_index);
205
733k
#endif
206
207
733k
  return G_UNLIKELY (p_index) ? *p_index : NULL;
208
733k
}
209
210
static guint
211
_datalist_index_hash (gconstpointer key)
212
0
{
213
0
  const GQuark *ptr = key;
214
215
0
  G_STATIC_ASSERT (G_STRUCT_OFFSET (GDataElt, key) == 0);
216
217
0
  return *ptr;
218
0
}
219
220
static gboolean
221
_datalist_index_equal (gconstpointer a, gconstpointer b)
222
0
{
223
0
  const GQuark *ptr_a = a;
224
0
  const GQuark *ptr_b = b;
225
226
0
  return *ptr_a == *ptr_b;
227
0
}
228
229
G_ALWAYS_INLINE static inline GHashTable *
230
datalist_index_new (void)
231
0
{
232
0
  return g_hash_table_new (_datalist_index_hash, _datalist_index_equal);
233
0
}
234
235
static GData *
236
datalist_realloc (GData *data, guint32 alloc, gboolean *out_reallocated)
237
0
{
238
0
  guintptr data_old;
239
0
  gboolean reallocated;
240
0
  GHashTable *index;
241
0
  GHashTable **p_index;
242
0
  guint32 i;
243
244
0
  data_old = (guintptr) ((gpointer) data);
245
0
  index = datalist_index_get (data);
246
247
0
  data = g_realloc (data, datalist_alloc_size (alloc));
248
249
  /* Determine whether realloc() moves the pointer. After a move, the old
250
   * pointer would be dangling and comparing it would be undefined behavior.
251
   * Avoid that by casting to uintptr_t.
252
   */
253
0
  reallocated = (((guintptr) ((gpointer) (data))) != data_old);
254
255
0
  data->alloc = alloc;
256
257
0
  if (out_reallocated)
258
0
    *out_reallocated = reallocated;
259
260
  /* Note that if data was @reallocated, then @index contains only dangling pointers.
261
   * We can only destroy/remove-all, which we rely on not following those pointers. */
262
263
0
  p_index = datalist_index_get_ptr (data);
264
265
0
  if (G_LIKELY (!p_index))
266
0
    {
267
0
      if (G_UNLIKELY (index))
268
0
        g_hash_table_unref (index);
269
0
    }
270
0
  else if (!reallocated && index)
271
0
    {
272
      /* The index is still fine and the pointers are all still valid. We
273
       * can keep it. */
274
0
      *p_index = index;
275
0
    }
276
0
  else
277
0
    {
278
0
      if (G_UNLIKELY (index))
279
0
        {
280
          /* Note that the GHashTable's keys are now all dangling pointers!
281
           * We rely on remove-all to not following them. */
282
0
          g_hash_table_remove_all (index);
283
0
        }
284
0
      else
285
0
        index = datalist_index_new ();
286
287
0
      *p_index = index;
288
289
0
      for (i = 0; i < data->len; i++)
290
0
        g_hash_table_add (index, &data->data[i]);
291
0
    }
292
293
0
  return data;
294
0
}
295
296
static gboolean
297
datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
298
113k
{
299
113k
  GDataElt *data_elt;
300
113k
  GHashTable *index;
301
113k
  gboolean reallocated;
302
113k
  GData *d;
303
304
113k
  d = *data;
305
113k
  if (!d)
306
108k
    {
307
108k
      d = g_malloc (datalist_alloc_size (2u));
308
108k
      d->len = 0;
309
108k
      d->alloc = 2u;
310
311
108k
      if (2u >= ALLOC_THRESHOLD_INDEX)
312
0
        *(datalist_index_get_ptr (d)) = datalist_index_new ();
313
314
108k
      *data = d;
315
108k
      reallocated = TRUE;
316
108k
    }
317
5.34k
  else if (d->len == d->alloc)
318
0
    {
319
0
      guint32 alloc = d->alloc * 2u;
320
321
0
      if (G_UNLIKELY (alloc < d->alloc))
322
0
        {
323
0
          if (d->alloc == G_MAXUINT32)
324
0
            g_error ("GData cannot contain more than 4294967295 entries");
325
0
          alloc = G_MAXUINT32;
326
0
        }
327
0
      d = datalist_realloc (d, alloc, &reallocated);
328
0
      *data = d;
329
0
    }
330
5.34k
  else
331
5.34k
    reallocated = FALSE;
332
333
113k
  data_elt = &d->data[d->len];
334
113k
  *data_elt = (GDataElt){
335
113k
    .key = key_id,
336
113k
    .data = new_data,
337
113k
    .destroy = destroy_func,
338
113k
  };
339
113k
  d->len++;
340
341
113k
  index = datalist_index_get (d);
342
113k
  if (G_UNLIKELY (index))
343
0
    g_hash_table_add (index, data_elt);
344
345
113k
  return reallocated;
346
113k
}
347
348
static void
349
datalist_remove (GData *data, guint32 idx)
350
7.80k
{
351
7.80k
  GHashTable *index;
352
353
7.80k
#if G_ENABLE_DEBUG
354
7.80k
  g_assert (idx < data->len);
355
7.80k
#endif
356
357
  /* We remove the element similar to g_array_remove_index_fast(). That is, the
358
   * entries up to @idx are left unchanged, and the last entry is moved to
359
   * position @idx.
360
   */
361
362
7.80k
  index = datalist_index_get (data);
363
7.80k
  if (G_UNLIKELY (index))
364
0
    g_hash_table_remove (index, &data->data[idx]);
365
366
7.80k
  data->len--;
367
368
7.80k
  if (idx != data->len)
369
0
    {
370
0
      data->data[idx] = data->data[data->len];
371
0
      if (G_UNLIKELY (index))
372
0
        g_hash_table_add (index, &data->data[idx]);
373
0
    }
374
7.80k
}
375
376
static gboolean
377
datalist_shrink (GData **data, GData **d_to_free)
378
7.80k
{
379
7.80k
  gboolean reallocated;
380
7.80k
  guint32 alloc_by_4;
381
7.80k
  guint32 v;
382
7.80k
  GData *d;
383
384
7.80k
  d = *data;
385
386
7.80k
  alloc_by_4 = d->alloc / 4u;
387
388
7.80k
  if (G_LIKELY (d->len > alloc_by_4))
389
0
    {
390
      /* No shrinking */
391
0
      return FALSE;
392
0
    }
393
394
7.80k
  if (d->len == 0)
395
7.80k
    {
396
7.80k
      GHashTable *index;
397
398
      /* The list became empty. We drop the allocated memory altogether. */
399
400
      /* The caller will free the buffer after releasing the lock, to minimize
401
       * the time we hold the lock. Transfer it out. */
402
403
7.80k
      index = datalist_index_get (d);
404
7.80k
      if (G_UNLIKELY (index))
405
0
        g_hash_table_unref (index);
406
407
7.80k
      *d_to_free = d;
408
7.80k
      *data = NULL;
409
7.80k
      return TRUE;
410
7.80k
    }
411
412
  /* If the buffer is filled not more than 25%. Shrink to double the current length. */
413
414
0
  v = d->len;
415
0
  if (v != alloc_by_4)
416
0
    {
417
      /* d->alloc is a power of two (unless it's G_MAXUINT32). Usually, we
418
       * remove one element at a time, then we will just reach reach a quarter
419
       * of that.
420
       *
421
       * However, with g_datalist_id_remove_multiple(), len can be smaller
422
       * at once. In that case, find first the next power of two. */
423
0
      v = g_nearest_pow (v);
424
0
    }
425
0
  v *= 2u;
426
427
0
#if G_ENABLE_DEBUG
428
0
  g_assert (v > d->len);
429
0
  g_assert (v <= (d->alloc == G_MAXUINT32 ? 0x80000000u : d->alloc / 2u));
430
0
#endif
431
432
0
  d = datalist_realloc (d, v, &reallocated);
433
0
  *d_to_free = NULL;
434
0
  *data = d;
435
0
  return reallocated;
436
0
}
437
438
static void
439
datalist_destroy (GData *data)
440
88.9k
{
441
88.9k
  GHashTable *index;
442
88.9k
  guint32 i;
443
444
  /* Must be called without lock. Will free @data and invoke the
445
   * destroy() notifications. */
446
447
88.9k
  index = datalist_index_get (data);
448
88.9k
  if (G_UNLIKELY (index))
449
0
    g_hash_table_unref (index);
450
451
183k
  for (i = 0; i < data->len; i++)
452
94.2k
    {
453
94.2k
      if (data->data[i].destroy)
454
94.2k
        data->data[i].destroy (data->data[i].data);
455
94.2k
    }
456
457
88.9k
  g_free (data);
458
88.9k
}
459
460
static GDataElt *
461
datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
462
715k
{
463
715k
  GDataElt *data_elt;
464
715k
  GHashTable *index;
465
715k
  guint32 i;
466
467
715k
  if (G_UNLIKELY (!data))
468
199k
    return NULL;
469
470
515k
  index = datalist_index_get (data);
471
472
515k
  if (G_LIKELY (!index))
473
515k
    {
474
      /* We have no index. Do a linear search. */
475
903k
      for (i = 0; i < data->len; i++)
476
537k
        {
477
537k
          data_elt = &data->data[i];
478
537k
          if (data_elt->key == key_id)
479
149k
            {
480
149k
              if (out_idx)
481
7.80k
                *out_idx = i;
482
149k
              return data_elt;
483
149k
            }
484
537k
        }
485
486
366k
      return NULL;
487
515k
    }
488
489
0
  data_elt = g_hash_table_lookup (index, &key_id);
490
0
  if (!data_elt)
491
0
    return NULL;
492
493
0
#if G_ENABLE_DEBUG
494
0
  g_assert (data_elt >= data->data && data_elt < &data->data[data->len]);
495
0
#endif
496
497
0
  if (out_idx)
498
0
    *out_idx = (data_elt - data->data);
499
0
  return data_elt;
500
0
}
501
502
/**
503
 * g_datalist_clear: (skip)
504
 * @datalist: a datalist.
505
 *
506
 * Frees all the data elements of the datalist.
507
 * The data elements' destroy functions are called
508
 * if they have been set.
509
 **/
510
void
511
g_datalist_clear (GData **datalist)
512
88.9k
{
513
88.9k
  GData *data;
514
515
88.9k
  g_return_if_fail (datalist != NULL);
516
517
88.9k
  data = g_datalist_lock_and_get (datalist);
518
519
88.9k
  if (!data)
520
0
    {
521
0
      g_datalist_unlock (datalist);
522
0
      return;
523
0
    }
524
525
88.9k
  g_datalist_unlock_and_set (datalist, NULL);
526
527
88.9k
  datalist_destroy (data);
528
88.9k
}
529
530
/* HOLDS: g_dataset_global_lock */
531
static inline GDataset*
532
g_dataset_lookup (gconstpointer dataset_location)
533
0
{
534
0
  GDataset *dataset;
535
  
536
0
  if (g_dataset_cached && g_dataset_cached->location == dataset_location)
537
0
    return g_dataset_cached;
538
  
539
0
  dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
540
0
  if (dataset)
541
0
    g_dataset_cached = dataset;
542
  
543
0
  return dataset;
544
0
}
545
546
/* HOLDS: g_dataset_global_lock */
547
static void
548
g_dataset_destroy_internal (GDataset *dataset)
549
0
{
550
0
  gconstpointer dataset_location;
551
  
552
0
  dataset_location = dataset->location;
553
0
  while (dataset)
554
0
    {
555
0
      GData *data;
556
557
0
      data = G_DATALIST_GET_POINTER (&dataset->datalist);
558
559
0
      if (!data)
560
0
  {
561
0
    if (dataset == g_dataset_cached)
562
0
      g_dataset_cached = NULL;
563
0
    g_hash_table_remove (g_dataset_location_ht, dataset_location);
564
0
    g_slice_free (GDataset, dataset);
565
0
    break;
566
0
  }
567
568
0
      G_DATALIST_SET_POINTER (&dataset->datalist, NULL);
569
570
0
      G_UNLOCK (g_dataset_global);
571
572
0
      datalist_destroy (data);
573
574
0
      G_LOCK (g_dataset_global);
575
0
      dataset = g_dataset_lookup (dataset_location);
576
0
    }
577
0
}
578
579
/**
580
 * g_dataset_destroy:
581
 * @dataset_location: (not nullable): the location identifying the dataset.
582
 *
583
 * Destroys the dataset, freeing all memory allocated, and calling any
584
 * destroy functions set for data elements.
585
 */
586
void
587
g_dataset_destroy (gconstpointer  dataset_location)
588
0
{
589
0
  g_return_if_fail (dataset_location != NULL);
590
  
591
0
  G_LOCK (g_dataset_global);
592
0
  if (g_dataset_location_ht)
593
0
    {
594
0
      GDataset *dataset;
595
596
0
      dataset = g_dataset_lookup (dataset_location);
597
0
      if (dataset)
598
0
  g_dataset_destroy_internal (dataset);
599
0
    }
600
0
  G_UNLOCK (g_dataset_global);
601
0
}
602
603
/* HOLDS: g_dataset_global_lock if dataset != null */
604
static inline gpointer
605
g_data_set_internal (GData    **datalist,
606
         GQuark         key_id,
607
         gpointer       new_data,
608
         GDestroyNotify new_destroy_func,
609
         GDataset    *dataset)
610
477k
{
611
477k
  GData *d;
612
477k
  GData *new_d = NULL;
613
477k
  GDataElt old, *data;
614
477k
  guint32 idx;
615
616
477k
  d = g_datalist_lock_and_get (datalist);
617
618
477k
  data = datalist_find (d, key_id, &idx);
619
620
477k
  if (new_data == NULL) /* remove */
621
363k
    {
622
363k
      if (data)
623
7.80k
        {
624
7.80k
          GData *d_to_free;
625
626
7.80k
          old = *data;
627
628
7.80k
          datalist_remove (d, idx);
629
7.80k
          if (datalist_shrink (&d, &d_to_free))
630
7.80k
            {
631
7.80k
              g_datalist_unlock_and_set (datalist, d);
632
633
              /* the dataset destruction *must* be done
634
               * prior to invocation of the data destroy function
635
               */
636
7.80k
              if (dataset && !d)
637
0
                g_dataset_destroy_internal (dataset);
638
639
7.80k
              if (d_to_free)
640
7.80k
                g_free (d_to_free);
641
7.80k
            }
642
0
          else
643
0
            g_datalist_unlock (datalist);
644
645
          /* We found and removed an old value
646
           * the GData struct *must* already be unlinked
647
           * when invoking the destroy function.
648
           * we use (new_data==NULL && new_destroy_func!=NULL) as
649
           * a special hint combination to "steal"
650
           * data without destroy notification
651
           */
652
7.80k
          if (old.destroy && !new_destroy_func)
653
0
            {
654
0
              if (dataset)
655
0
                G_UNLOCK (g_dataset_global);
656
0
              old.destroy (old.data);
657
0
              if (dataset)
658
0
                G_LOCK (g_dataset_global);
659
0
              old.data = NULL;
660
0
            }
661
662
7.80k
          return old.data;
663
7.80k
        }
664
363k
    }
665
113k
  else
666
113k
    {
667
113k
      if (data)
668
0
        {
669
0
          if (!data->destroy)
670
0
            {
671
0
              data->data = new_data;
672
0
              data->destroy = new_destroy_func;
673
0
              g_datalist_unlock (datalist);
674
0
            }
675
0
          else
676
0
            {
677
0
              old = *data;
678
0
              data->data = new_data;
679
0
              data->destroy = new_destroy_func;
680
681
0
              g_datalist_unlock (datalist);
682
683
              /* We found and replaced an old value
684
               * the GData struct *must* already be unlinked
685
               * when invoking the destroy function.
686
               */
687
0
              if (dataset)
688
0
                G_UNLOCK (g_dataset_global);
689
0
              old.destroy (old.data);
690
0
              if (dataset)
691
0
                G_LOCK (g_dataset_global);
692
0
            }
693
0
          return NULL;
694
0
        }
695
696
      /* The key was not found, insert it */
697
113k
      if (datalist_append (&d, key_id, new_data, new_destroy_func))
698
108k
        new_d = d;
699
113k
    }
700
701
469k
  if (new_d)
702
108k
    g_datalist_unlock_and_set (datalist, new_d);
703
361k
  else
704
361k
    g_datalist_unlock (datalist);
705
706
469k
  return NULL;
707
708
477k
}
709
710
/**
711
 * g_datalist_id_remove_multiple:
712
 * @datalist: a datalist
713
 * @keys: (array length=n_keys): keys to remove
714
 * @n_keys: length of @keys.
715
 *
716
 * Removes multiple keys from a datalist.
717
 *
718
 * This is more efficient than calling g_datalist_id_remove_data()
719
 * multiple times in a row.
720
 *
721
 * Before 2.80, @n_keys had to be not larger than 16.
722
 * Since 2.84, performance is improved for larger number of keys.
723
 *
724
 * Since: 2.74
725
 */
726
void
727
g_datalist_id_remove_multiple (GData **datalist,
728
                               GQuark *keys,
729
                               gsize n_keys)
730
0
{
731
0
  GData *d;
732
0
  GDataElt *old;
733
0
  GDataElt *old_to_free = NULL;
734
0
  GData *d_to_free;
735
0
  gsize found_keys;
736
0
  gsize i_keys;
737
738
0
  if (n_keys == 0)
739
0
    return;
740
741
0
  d = g_datalist_lock_and_get (datalist);
742
743
0
  if (!d)
744
0
    {
745
0
      g_datalist_unlock (datalist);
746
0
      return;
747
0
    }
748
749
  /* Allocate an array of GDataElt to hold copies of the elements
750
   * that are removed from the datalist. Allow enough space for all
751
   * the keys.
752
   *
753
   * At most allocate 400 bytes on the stack. Especially since we call
754
   * out to external code, we don't know how much stack we can use. */
755
0
  if (n_keys <= 400u / sizeof (GDataElt))
756
0
    old = g_newa (GDataElt, n_keys);
757
0
  else
758
0
    {
759
0
      old_to_free = g_new (GDataElt, n_keys);
760
0
      old = old_to_free;
761
0
    }
762
763
0
  found_keys = 0;
764
0
  for (i_keys = 0; i_keys < n_keys; i_keys++)
765
0
    {
766
0
      GDataElt *data_elt;
767
0
      guint32 idx;
768
769
0
      data_elt = datalist_find (d, keys[i_keys], &idx);
770
0
      if (!data_elt)
771
0
        continue;
772
773
      /* We must destroy the keys in the order in which they are specified.
774
       * We achieve that here.
775
       *
776
       * Note that even if @keys contains duplicates, we correctly only
777
       * find them once, as we remove the found entry right away. */
778
0
      old[found_keys++] = *data_elt;
779
0
      datalist_remove (d, idx);
780
0
    }
781
782
0
  if (found_keys > 0 && datalist_shrink (&d, &d_to_free))
783
0
    {
784
0
      g_datalist_unlock_and_set (datalist, d);
785
0
      if (d_to_free)
786
0
        g_free (d_to_free);
787
0
    }
788
0
  else
789
0
    g_datalist_unlock (datalist);
790
791
0
  for (i_keys = 0; i_keys < found_keys; i_keys++)
792
0
    {
793
0
      if (old[i_keys].destroy)
794
0
        old[i_keys].destroy (old[i_keys].data);
795
0
    }
796
797
0
  if (G_UNLIKELY (old_to_free))
798
0
    g_free (old_to_free);
799
0
}
800
801
/**
802
 * g_dataset_id_set_data_full: (skip)
803
 * @dataset_location: (not nullable): the location identifying the dataset.
804
 * @key_id: the #GQuark id to identify the data element.
805
 * @data: the data element.
806
 * @destroy_func: the function to call when the data element is
807
 *                removed. This function will be called with the data
808
 *                element and can be used to free any memory allocated
809
 *                for it.
810
 *
811
 * Sets the data element associated with the given #GQuark id, and also
812
 * the function to call when the data element is destroyed. Any
813
 * previous data with the same key is removed, and its destroy function
814
 * is called.
815
 **/
816
/**
817
 * g_dataset_set_data_full: (skip)
818
 * @l: the location identifying the dataset.
819
 * @k: the string to identify the data element.
820
 * @d: the data element.
821
 * @f: the function to call when the data element is removed. This
822
 *     function will be called with the data element and can be used to
823
 *     free any memory allocated for it.
824
 *
825
 * Sets the data corresponding to the given string identifier, and the
826
 * function to call when the data element is destroyed.
827
 **/
828
/**
829
 * g_dataset_id_set_data:
830
 * @l: the location identifying the dataset.
831
 * @k: the #GQuark id to identify the data element.
832
 * @d: the data element.
833
 *
834
 * Sets the data element associated with the given #GQuark id. Any
835
 * previous data with the same key is removed, and its destroy function
836
 * is called.
837
 **/
838
/**
839
 * g_dataset_set_data:
840
 * @l: the location identifying the dataset.
841
 * @k: the string to identify the data element.
842
 * @d: the data element.
843
 *
844
 * Sets the data corresponding to the given string identifier.
845
 **/
846
/**
847
 * g_dataset_id_remove_data:
848
 * @l: the location identifying the dataset.
849
 * @k: the #GQuark id identifying the data element.
850
 *
851
 * Removes a data element from a dataset. The data element's destroy
852
 * function is called if it has been set.
853
 **/
854
/**
855
 * g_dataset_remove_data:
856
 * @l: the location identifying the dataset.
857
 * @k: the string identifying the data element.
858
 *
859
 * Removes a data element corresponding to a string. Its destroy
860
 * function is called if it has been set.
861
 **/
862
void
863
g_dataset_id_set_data_full (gconstpointer  dataset_location,
864
          GQuark         key_id,
865
          gpointer       data,
866
          GDestroyNotify destroy_func)
867
0
{
868
0
  GDataset *dataset;
869
  
870
0
  g_return_if_fail (dataset_location != NULL);
871
0
  if (!data)
872
0
    g_return_if_fail (destroy_func == NULL);
873
0
  if (!key_id)
874
0
    {
875
0
      if (data)
876
0
  g_return_if_fail (key_id > 0);
877
0
      else
878
0
  return;
879
0
    }
880
  
881
0
  G_LOCK (g_dataset_global);
882
0
  if (!g_dataset_location_ht)
883
0
    g_data_initialize ();
884
 
885
0
  dataset = g_dataset_lookup (dataset_location);
886
0
  if (!dataset)
887
0
    {
888
0
      dataset = g_slice_new (GDataset);
889
0
      dataset->location = dataset_location;
890
0
      g_datalist_init (&dataset->datalist);
891
0
      g_hash_table_insert (g_dataset_location_ht, 
892
0
         (gpointer) dataset->location,
893
0
         dataset);
894
0
    }
895
  
896
0
  g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
897
0
  G_UNLOCK (g_dataset_global);
898
0
}
899
900
/**
901
 * g_datalist_id_set_data_full: (skip)
902
 * @datalist: a datalist.
903
 * @key_id: the #GQuark to identify the data element.
904
 * @data: (nullable): the data element or %NULL to remove any previous element
905
 *        corresponding to @key_id.
906
 * @destroy_func: (nullable): the function to call when the data element is
907
 *                removed. This function will be called with the data
908
 *                element and can be used to free any memory allocated
909
 *                for it. If @data is %NULL, then @destroy_func must
910
 *                also be %NULL.
911
 *
912
 * Sets the data corresponding to the given #GQuark id, and the
913
 * function to be called when the element is removed from the datalist.
914
 * Any previous data with the same key is removed, and its destroy
915
 * function is called.
916
 **/
917
/**
918
 * g_datalist_set_data_full: (skip)
919
 * @dl: a datalist.
920
 * @k: the string to identify the data element.
921
 * @d: (nullable): the data element, or %NULL to remove any previous element
922
 *     corresponding to @k.
923
 * @f: (nullable): the function to call when the data element is removed.
924
 *     This function will be called with the data element and can be used to
925
 *     free any memory allocated for it. If @d is %NULL, then @f must
926
 *     also be %NULL.
927
 *
928
 * Sets the data element corresponding to the given string identifier,
929
 * and the function to be called when the data element is removed.
930
 **/
931
/**
932
 * g_datalist_id_set_data:
933
 * @dl: a datalist.
934
 * @q: the #GQuark to identify the data element.
935
 * @d: (nullable): the data element, or %NULL to remove any previous element
936
 *     corresponding to @q.
937
 *
938
 * Sets the data corresponding to the given #GQuark id. Any previous
939
 * data with the same key is removed, and its destroy function is
940
 * called.
941
 **/
942
/**
943
 * g_datalist_set_data:
944
 * @dl: a datalist.
945
 * @k: the string to identify the data element.
946
 * @d: (nullable): the data element, or %NULL to remove any previous element
947
 *     corresponding to @k.
948
 *
949
 * Sets the data element corresponding to the given string identifier.
950
 **/
951
/**
952
 * g_datalist_id_remove_data:
953
 * @dl: a datalist.
954
 * @q: the #GQuark identifying the data element.
955
 *
956
 * Removes an element, using its #GQuark identifier.
957
 **/
958
/**
959
 * g_datalist_remove_data:
960
 * @dl: a datalist.
961
 * @k: the string identifying the data element.
962
 *
963
 * Removes an element using its string identifier. The data element's
964
 * destroy function is called if it has been set.
965
 **/
966
void
967
g_datalist_id_set_data_full (GData    **datalist,
968
           GQuark         key_id,
969
           gpointer       data,
970
           GDestroyNotify destroy_func)
971
469k
{
972
469k
  g_return_if_fail (datalist != NULL);
973
469k
  if (!data)
974
469k
    g_return_if_fail (destroy_func == NULL);
975
469k
  if (!key_id)
976
0
    {
977
0
      if (data)
978
0
  g_return_if_fail (key_id > 0);
979
0
      else
980
0
  return;
981
0
    }
982
983
469k
  g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
984
469k
}
985
986
/**
987
 * g_dataset_id_remove_no_notify: (skip)
988
 * @dataset_location: (not nullable): the location identifying the dataset.
989
 * @key_id: the #GQuark ID identifying the data element.
990
 *
991
 * Removes an element, without calling its destroy notification
992
 * function.
993
 *
994
 * Returns: (nullable): the data previously stored at @key_id,
995
 *          or %NULL if none.
996
 **/
997
/**
998
 * g_dataset_remove_no_notify: (skip)
999
 * @l: the location identifying the dataset.
1000
 * @k: the string identifying the data element.
1001
 *
1002
 * Removes an element, without calling its destroy notifier.
1003
 **/
1004
gpointer
1005
g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
1006
             GQuark         key_id)
1007
0
{
1008
0
  gpointer ret_data = NULL;
1009
1010
0
  g_return_val_if_fail (dataset_location != NULL, NULL);
1011
  
1012
0
  G_LOCK (g_dataset_global);
1013
0
  if (key_id && g_dataset_location_ht)
1014
0
    {
1015
0
      GDataset *dataset;
1016
  
1017
0
      dataset = g_dataset_lookup (dataset_location);
1018
0
      if (dataset)
1019
0
  ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
1020
0
    } 
1021
0
  G_UNLOCK (g_dataset_global);
1022
1023
0
  return ret_data;
1024
0
}
1025
1026
/**
1027
 * g_datalist_id_remove_no_notify: (skip)
1028
 * @datalist: a datalist.
1029
 * @key_id: the #GQuark identifying a data element.
1030
 *
1031
 * Removes an element, without calling its destroy notification
1032
 * function.
1033
 *
1034
 * Returns: (nullable): the data previously stored at @key_id,
1035
 *          or %NULL if none.
1036
 **/
1037
/**
1038
 * g_datalist_remove_no_notify: (skip)
1039
 * @dl: a datalist.
1040
 * @k: the string identifying the data element.
1041
 *
1042
 * Removes an element, without calling its destroy notifier.
1043
 **/
1044
gpointer
1045
g_datalist_id_remove_no_notify (GData **datalist,
1046
        GQuark    key_id)
1047
7.80k
{
1048
7.80k
  gpointer ret_data = NULL;
1049
1050
7.80k
  g_return_val_if_fail (datalist != NULL, NULL);
1051
1052
7.80k
  if (key_id)
1053
7.80k
    ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
1054
1055
7.80k
  return ret_data;
1056
7.80k
}
1057
1058
/*< private >
1059
 * g_datalist_id_update_atomic:
1060
 * @datalist: the data list
1061
 * @key_id: the key to add.
1062
 * @callback: (scope call): callback to update (set, remove, steal, update) the
1063
 *   data.
1064
 * @user_data: the user data for @callback.
1065
 *
1066
 * Will call @callback while holding the lock on @datalist. Be careful to not
1067
 * end up calling into another data-list function, because the lock is not
1068
 * reentrant and deadlock will happen.
1069
 *
1070
 * The callback receives the current data and destroy function. If @key_id is
1071
 * currently not in @datalist, they will be %NULL. The callback can update
1072
 * those pointers, and @datalist will be updated with the result. Note that if
1073
 * callback modifies a received data, then it MUST steal it and take ownership
1074
 * on it. Possibly by freeing it with the provided destroy function.
1075
 *
1076
 * The point is to atomically access the entry, while holding a lock
1077
 * of @datalist. Without this, the user would have to hold their own mutex
1078
 * while handling @key_id entry.
1079
 *
1080
 * The return value of @callback is not used, except it becomes the return
1081
 * value of the function. This is an alternative to returning a result via
1082
 * @user_data.
1083
 *
1084
 * Returns: the value returned by @callback.
1085
 *
1086
 * Since: 2.80
1087
 */
1088
gpointer
1089
g_datalist_id_update_atomic (GData **datalist,
1090
                             GQuark key_id,
1091
                             GDataListUpdateAtomicFunc callback,
1092
                             gpointer user_data)
1093
1
{
1094
1
  GData *d;
1095
1
  GDataElt *data;
1096
1
  gpointer new_data;
1097
1
  gpointer result;
1098
1
  GDestroyNotify new_destroy;
1099
1
  guint32 idx;
1100
1
  gboolean to_unlock = TRUE;
1101
1102
1
  d = g_datalist_lock_and_get (datalist);
1103
1104
1
  data = datalist_find (d, key_id, &idx);
1105
1106
1
  if (data)
1107
0
    {
1108
0
      new_data = data->data;
1109
0
      new_destroy = data->destroy;
1110
0
    }
1111
1
  else
1112
1
    {
1113
1
      new_data = NULL;
1114
1
      new_destroy = NULL;
1115
1
    }
1116
1117
1
  result = callback (&new_data, &new_destroy, user_data);
1118
1119
1
  if (data && !new_data)
1120
0
    {
1121
0
      GData *d_to_free;
1122
1123
      /* Remove. The callback indicates to drop the entry.
1124
       *
1125
       * The old data->data was stolen by callback(). */
1126
0
      datalist_remove (d, idx);
1127
0
      if (datalist_shrink (&d, &d_to_free))
1128
0
        {
1129
0
          g_datalist_unlock_and_set (datalist, d);
1130
0
          if (d_to_free)
1131
0
            g_free (d_to_free);
1132
0
          to_unlock = FALSE;
1133
0
        }
1134
0
    }
1135
1
  else if (data)
1136
0
    {
1137
      /* Update. The callback may have provided new pointers to an existing
1138
       * entry.
1139
       *
1140
       * The old data was stolen by callback(). We only update the pointers and
1141
       * are done. */
1142
0
      data->data = new_data;
1143
0
      data->destroy = new_destroy;
1144
0
    }
1145
1
  else if (!data && !new_data)
1146
0
    {
1147
      /* Absent. No change. The entry didn't exist and still does not. */
1148
0
    }
1149
1
  else
1150
1
    {
1151
      /* Add. Add a new entry that didn't exist previously. */
1152
1
      if (datalist_append (&d, key_id, new_data, new_destroy))
1153
1
        {
1154
1
          g_datalist_unlock_and_set (datalist, d);
1155
1
          to_unlock = FALSE;
1156
1
        }
1157
1
    }
1158
1159
1
  if (to_unlock)
1160
0
    g_datalist_unlock (datalist);
1161
1162
1
  return result;
1163
1
}
1164
1165
/**
1166
 * g_dataset_id_get_data:
1167
 * @dataset_location: (not nullable): the location identifying the dataset.
1168
 * @key_id: the #GQuark id to identify the data element.
1169
 *
1170
 * Gets the data element corresponding to a #GQuark.
1171
 *
1172
 * Returns: (transfer none) (nullable): the data element corresponding to
1173
 *          the #GQuark, or %NULL if it is not found.
1174
 **/
1175
/**
1176
 * g_dataset_get_data:
1177
 * @l: the location identifying the dataset.
1178
 * @k: the string identifying the data element.
1179
 *
1180
 * Gets the data element corresponding to a string.
1181
 *
1182
 * Returns: (transfer none) (nullable): the data element corresponding to
1183
 *          the string, or %NULL if it is not found.
1184
 **/
1185
gpointer
1186
g_dataset_id_get_data (gconstpointer  dataset_location,
1187
           GQuark         key_id)
1188
0
{
1189
0
  gpointer retval = NULL;
1190
1191
0
  g_return_val_if_fail (dataset_location != NULL, NULL);
1192
  
1193
0
  G_LOCK (g_dataset_global);
1194
0
  if (key_id && g_dataset_location_ht)
1195
0
    {
1196
0
      GDataset *dataset;
1197
      
1198
0
      dataset = g_dataset_lookup (dataset_location);
1199
0
      if (dataset)
1200
0
  retval = g_datalist_id_get_data (&dataset->datalist, key_id);
1201
0
    }
1202
0
  G_UNLOCK (g_dataset_global);
1203
 
1204
0
  return retval;
1205
0
}
1206
1207
/**
1208
 * g_datalist_id_get_data:
1209
 * @datalist: a datalist.
1210
 * @key_id: the #GQuark identifying a data element.
1211
 *
1212
 * Retrieves the data element corresponding to @key_id.
1213
 *
1214
 * Returns: (transfer none) (nullable): the data element, or %NULL if
1215
 *          it is not found.
1216
 */
1217
gpointer
1218
g_datalist_id_get_data (GData  **datalist,
1219
      GQuark   key_id)
1220
238k
{
1221
238k
  return g_datalist_id_dup_data (datalist, key_id, NULL, NULL);
1222
238k
}
1223
1224
/**
1225
 * GDuplicateFunc:
1226
 * @data: the data to duplicate
1227
 * @user_data: (closure): user data that was specified in
1228
 *             g_datalist_id_dup_data()
1229
 *
1230
 * The type of functions that are used to 'duplicate' an object.
1231
 * What this means depends on the context, it could just be
1232
 * incrementing the reference count, if @data is a ref-counted
1233
 * object.
1234
 *
1235
 * Returns: a duplicate of data
1236
 */
1237
1238
/**
1239
 * g_datalist_id_dup_data: (skip)
1240
 * @datalist: location of a datalist
1241
 * @key_id: the #GQuark identifying a data element
1242
 * @dup_func: (scope call) (closure user_data) (nullable): function to
1243
 *   duplicate the old value
1244
 * @user_data: passed as user_data to @dup_func
1245
 *
1246
 * This is a variant of g_datalist_id_get_data() which
1247
 * returns a 'duplicate' of the value. @dup_func defines the
1248
 * meaning of 'duplicate' in this context, it could e.g.
1249
 * take a reference on a ref-counted object.
1250
 *
1251
 * If the @key_id is not set in the datalist then @dup_func
1252
 * will be called with a %NULL argument.
1253
 *
1254
 * Note that @dup_func is called while the datalist is locked, so it
1255
 * is not allowed to read or modify the datalist.
1256
 *
1257
 * This function can be useful to avoid races when multiple
1258
 * threads are using the same datalist and the same key.
1259
 *
1260
 * Returns: (nullable): the result of calling @dup_func on the value
1261
 *     associated with @key_id in @datalist, or %NULL if not set.
1262
 *     If @dup_func is %NULL, the value is returned unmodified.
1263
 *
1264
 * Since: 2.34
1265
 */
1266
gpointer
1267
g_datalist_id_dup_data (GData          **datalist,
1268
                        GQuark           key_id,
1269
                        GDuplicateFunc   dup_func,
1270
                        gpointer         user_data)
1271
238k
{
1272
238k
  gpointer val = NULL;
1273
238k
  gpointer retval = NULL;
1274
238k
  GData *d;
1275
238k
  GDataElt *data;
1276
1277
238k
  d = g_datalist_lock_and_get (datalist);
1278
1279
238k
  data = datalist_find (d, key_id, NULL);
1280
238k
  if (data)
1281
141k
    val = data->data;
1282
1283
238k
  if (dup_func)
1284
0
    retval = dup_func (val, user_data);
1285
238k
  else
1286
238k
    retval = val;
1287
1288
238k
  g_datalist_unlock (datalist);
1289
1290
238k
  return retval;
1291
238k
}
1292
1293
/**
1294
 * g_datalist_id_replace_data: (skip)
1295
 * @datalist: location of a datalist
1296
 * @key_id: the #GQuark identifying a data element
1297
 * @oldval: (nullable): the old value to compare against
1298
 * @newval: (nullable): the new value to replace it with
1299
 * @destroy: (nullable): destroy notify for the new value
1300
 * @old_destroy: (out) (optional): destroy notify for the existing value
1301
 *
1302
 * Compares the member that is associated with @key_id in
1303
 * @datalist to @oldval, and if they are the same, replace
1304
 * @oldval with @newval.
1305
 *
1306
 * This is like a typical atomic compare-and-exchange
1307
 * operation, for a member of @datalist.
1308
 *
1309
 * If the previous value was replaced then ownership of the
1310
 * old value (@oldval) is passed to the caller, including
1311
 * the registered destroy notify for it (passed out in @old_destroy).
1312
 * Its up to the caller to free this as they wish, which may
1313
 * or may not include using @old_destroy as sometimes replacement
1314
 * should not destroy the object in the normal way.
1315
 *
1316
 * Returns: %TRUE if the existing value for @key_id was replaced
1317
 *  by @newval, %FALSE otherwise.
1318
 *
1319
 * Since: 2.34
1320
 */
1321
gboolean
1322
g_datalist_id_replace_data (GData          **datalist,
1323
                            GQuark           key_id,
1324
                            gpointer         oldval,
1325
                            gpointer         newval,
1326
                            GDestroyNotify   destroy,
1327
                            GDestroyNotify  *old_destroy)
1328
0
{
1329
0
  gpointer val = NULL;
1330
0
  GData *d;
1331
0
  GDataElt *data;
1332
0
  GData *d_to_free = NULL;
1333
0
  gboolean set_d = FALSE;
1334
0
  guint32 idx;
1335
1336
0
  g_return_val_if_fail (datalist != NULL, FALSE);
1337
0
  g_return_val_if_fail (key_id != 0, FALSE);
1338
1339
0
  if (old_destroy)
1340
0
    *old_destroy = NULL;
1341
1342
0
  d = g_datalist_lock_and_get (datalist);
1343
1344
0
  data = datalist_find (d, key_id, &idx);
1345
0
  if (data)
1346
0
    {
1347
0
      val = data->data;
1348
0
      if (val == oldval)
1349
0
        {
1350
0
          if (old_destroy)
1351
0
            *old_destroy = data->destroy;
1352
0
          if (newval != NULL)
1353
0
            {
1354
0
              data->data = newval;
1355
0
              data->destroy = destroy;
1356
0
            }
1357
0
          else
1358
0
            {
1359
0
              datalist_remove (d, idx);
1360
0
              if (datalist_shrink (&d, &d_to_free))
1361
0
                set_d = TRUE;
1362
0
            }
1363
0
        }
1364
0
    }
1365
1366
0
  if (val == NULL && oldval == NULL && newval != NULL)
1367
0
    {
1368
0
      if (datalist_append (&d, key_id, newval, destroy))
1369
0
        {
1370
0
          set_d = TRUE;
1371
0
        }
1372
0
    }
1373
1374
0
  if (set_d)
1375
0
    g_datalist_unlock_and_set (datalist, d);
1376
0
  else
1377
0
    g_datalist_unlock (datalist);
1378
1379
0
  if (d_to_free)
1380
0
    g_free (d_to_free);
1381
1382
0
  return val == oldval;
1383
0
}
1384
1385
/**
1386
 * g_datalist_get_data:
1387
 * @datalist: a datalist.
1388
 * @key: the string identifying a data element.
1389
 *
1390
 * Gets a data element, using its string identifier. This is slower than
1391
 * g_datalist_id_get_data() because it compares strings.
1392
 *
1393
 * Returns: (transfer none) (nullable): the data element, or %NULL if it
1394
 *          is not found.
1395
 **/
1396
gpointer
1397
g_datalist_get_data (GData **datalist,
1398
                     const gchar *key)
1399
0
{
1400
0
  GQuark key_id;
1401
0
  GHashTable *index;
1402
0
  gpointer res = NULL;
1403
0
  GDataElt *data_elt;
1404
0
  GData *d;
1405
1406
0
  g_return_val_if_fail (datalist != NULL, NULL);
1407
1408
0
  d = g_datalist_lock_and_get (datalist);
1409
1410
0
  if (!d)
1411
0
    goto out;
1412
1413
0
  index = datalist_index_get (d);
1414
1415
0
  if (G_LIKELY (!index))
1416
0
    {
1417
0
      guint32 i;
1418
1419
0
      for (i = 0; i < d->len; i++)
1420
0
        {
1421
0
          data_elt = &d->data[i];
1422
          /* Here we intentionally compare by strings, instead of calling
1423
           * g_quark_try_string() first.
1424
           *
1425
           * See commit 1cceda49b60b ('Make g_datalist_get_data not look up the
1426
           * quark').
1427
           */
1428
0
          if (g_strcmp0 (g_quark_to_string (data_elt->key), key) == 0)
1429
0
            {
1430
0
              res = data_elt->data;
1431
0
              goto out;
1432
0
            }
1433
0
        }
1434
0
      goto out;
1435
0
    }
1436
1437
0
  key_id = g_quark_try_string (key);
1438
0
  if (key_id == 0 && key)
1439
0
    goto out;
1440
1441
0
  data_elt = g_hash_table_lookup (index, &key_id);
1442
1443
0
  if (data_elt)
1444
0
    res = data_elt->data;
1445
1446
0
out:
1447
0
  g_datalist_unlock (datalist);
1448
1449
0
  return res;
1450
0
}
1451
1452
/**
1453
 * GDataForeachFunc:
1454
 * @key_id: the #GQuark id to identifying the data element.
1455
 * @data: the data element.
1456
 * @user_data: (closure): user data passed to g_dataset_foreach().
1457
 *
1458
 * Specifies the type of function passed to g_dataset_foreach(). It is
1459
 * called with each #GQuark id and associated data element, together
1460
 * with the @user_data parameter supplied to g_dataset_foreach().
1461
 **/
1462
1463
/**
1464
 * g_dataset_foreach:
1465
 * @dataset_location: (not nullable): the location identifying the dataset.
1466
 * @func: (scope call) (closure user_data): the function to call for each data element.
1467
 * @user_data: user data to pass to the function.
1468
 *
1469
 * Calls the given function for each data element which is associated
1470
 * with the given location. Note that this function is NOT thread-safe.
1471
 * So unless @dataset_location can be protected from any modifications
1472
 * during invocation of this function, it should not be called.
1473
 *
1474
 * @func can make changes to the dataset, but the iteration will not
1475
 * reflect changes made during the g_dataset_foreach() call, other
1476
 * than skipping over elements that are removed.
1477
 **/
1478
void
1479
g_dataset_foreach (gconstpointer    dataset_location,
1480
       GDataForeachFunc func,
1481
       gpointer         user_data)
1482
0
{
1483
0
  GDataset *dataset;
1484
  
1485
0
  g_return_if_fail (dataset_location != NULL);
1486
0
  g_return_if_fail (func != NULL);
1487
1488
0
  G_LOCK (g_dataset_global);
1489
0
  if (g_dataset_location_ht)
1490
0
    {
1491
0
      dataset = g_dataset_lookup (dataset_location);
1492
0
      G_UNLOCK (g_dataset_global);
1493
0
      if (dataset)
1494
0
  g_datalist_foreach (&dataset->datalist, func, user_data);
1495
0
    }
1496
0
  else
1497
0
    {
1498
0
      G_UNLOCK (g_dataset_global);
1499
0
    }
1500
0
}
1501
1502
/**
1503
 * g_datalist_foreach:
1504
 * @datalist: a datalist.
1505
 * @func: (scope call) (closure user_data): the function to call for each data element.
1506
 * @user_data: user data to pass to the function.
1507
 *
1508
 * Calls the given function for each data element of the datalist. The
1509
 * function is called with each data element's #GQuark id and data,
1510
 * together with the given @user_data parameter. Note that this
1511
 * function is NOT thread-safe. So unless @datalist can be protected
1512
 * from any modifications during invocation of this function, it should
1513
 * not be called.
1514
 *
1515
 * @func can make changes to @datalist, but the iteration will not
1516
 * reflect changes made during the g_datalist_foreach() call, other
1517
 * than skipping over elements that are removed.
1518
 **/
1519
void
1520
g_datalist_foreach (GData    **datalist,
1521
        GDataForeachFunc func,
1522
        gpointer         user_data)
1523
0
{
1524
0
  GData *d;
1525
0
  guint i, j, len;
1526
0
  GQuark *keys;
1527
1528
0
  g_return_if_fail (datalist != NULL);
1529
0
  g_return_if_fail (func != NULL);
1530
1531
0
  d = G_DATALIST_GET_POINTER (datalist);
1532
0
  if (d == NULL) 
1533
0
    return;
1534
1535
  /* We make a copy of the keys so that we can handle it changing
1536
     in the callback */
1537
0
  len = d->len;
1538
0
  keys = g_new (GQuark, len);
1539
0
  for (i = 0; i < len; i++)
1540
0
    keys[i] = d->data[i].key;
1541
  
1542
0
  for (i = 0; i < len; i++)
1543
0
    {
1544
      /* A previous callback might have removed a later item, so always check that
1545
   it still exists before calling */
1546
0
      d = G_DATALIST_GET_POINTER (datalist);
1547
      
1548
0
      if (d == NULL)
1549
0
  break;
1550
0
      for (j = 0; j < d->len; j++)
1551
0
  {
1552
0
    if (d->data[j].key == keys[i]) {
1553
0
      func (d->data[i].key, d->data[i].data, user_data);
1554
0
      break;
1555
0
    }
1556
0
  }
1557
0
    }
1558
0
  g_free (keys);
1559
0
}
1560
1561
/**
1562
 * g_datalist_init: (skip)
1563
 * @datalist: a pointer to a pointer to a datalist.
1564
 *
1565
 * Resets the datalist to %NULL. It does not free any memory or call
1566
 * any destroy functions.
1567
 **/
1568
void
1569
g_datalist_init (GData **datalist)
1570
0
{
1571
0
  g_return_if_fail (datalist != NULL);
1572
1573
0
  g_atomic_pointer_set (datalist, NULL);
1574
0
}
1575
1576
/**
1577
 * g_datalist_set_flags:
1578
 * @datalist: pointer to the location that holds a list
1579
 * @flags: the flags to turn on. The values of the flags are
1580
 *   restricted by %G_DATALIST_FLAGS_MASK (currently
1581
 *   3; giving two possible boolean flags).
1582
 *   A value for @flags that doesn't fit within the mask is
1583
 *   an error.
1584
 * 
1585
 * Turns on flag values for a data list. This function is used
1586
 * to keep a small number of boolean flags in an object with
1587
 * a data list without using any additional space. It is
1588
 * not generally useful except in circumstances where space
1589
 * is very tight. (It is used in the base #GObject type, for
1590
 * example.)
1591
 *
1592
 * Since: 2.8
1593
 **/
1594
void
1595
g_datalist_set_flags (GData **datalist,
1596
          guint   flags)
1597
45
{
1598
45
  g_return_if_fail (datalist != NULL);
1599
45
  g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1600
1601
45
  g_atomic_pointer_or (datalist, (gsize)flags);
1602
45
}
1603
1604
/**
1605
 * g_datalist_unset_flags:
1606
 * @datalist: pointer to the location that holds a list
1607
 * @flags: the flags to turn off. The values of the flags are
1608
 *   restricted by %G_DATALIST_FLAGS_MASK (currently
1609
 *   3: giving two possible boolean flags).
1610
 *   A value for @flags that doesn't fit within the mask is
1611
 *   an error.
1612
 * 
1613
 * Turns off flag values for a data list. See g_datalist_unset_flags()
1614
 *
1615
 * Since: 2.8
1616
 **/
1617
void
1618
g_datalist_unset_flags (GData **datalist,
1619
      guint   flags)
1620
0
{
1621
0
  g_return_if_fail (datalist != NULL);
1622
0
  g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1623
1624
0
  g_atomic_pointer_and (datalist, ~(gsize)flags);
1625
0
}
1626
1627
/**
1628
 * g_datalist_get_flags:
1629
 * @datalist: pointer to the location that holds a list
1630
 * 
1631
 * Gets flags values packed in together with the datalist.
1632
 * See g_datalist_set_flags().
1633
 * 
1634
 * Returns: the flags of the datalist
1635
 *
1636
 * Since: 2.8
1637
 **/
1638
guint
1639
g_datalist_get_flags (GData **datalist)
1640
3.04M
{
1641
3.04M
  g_return_val_if_fail (datalist != NULL, 0);
1642
  
1643
3.04M
  return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
1644
3.04M
}
1645
1646
/* HOLDS: g_dataset_global_lock */
1647
static void
1648
g_data_initialize (void)
1649
0
{
1650
0
  g_return_if_fail (g_dataset_location_ht == NULL);
1651
1652
0
  g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
1653
  g_dataset_cached = NULL;
1654
0
}