Coverage Report

Created: 2025-06-13 06:21

/src/glib/glib/gdataset.c
Line
Count
Source (jump to first uncovered line)
1
/* GLIB - Library of useful routines for C programming
2
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3
 *
4
 * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
5
 * Copyright (C) 1998 Tim Janik
6
 *
7
 * SPDX-License-Identifier: LGPL-2.1-or-later
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public
11
 * License as published by the Free Software Foundation; either
12
 * version 2.1 of the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public
20
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/*
24
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
25
 * file for a list of people on the GLib Team.  See the ChangeLog
26
 * files for a list of changes.  These files are distributed with
27
 * GLib at ftp://ftp.gtk.org/pub/gtk/.
28
 */
29
30
/*
31
 * MT safe ; except for g_data*_foreach()
32
 */
33
34
#include "config.h"
35
36
#include <string.h>
37
38
#include "gdataset.h"
39
#include "gbitlock.h"
40
41
#include "gslice.h"
42
#include "gdatasetprivate.h"
43
#include "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
38.9k
#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
6.48k
#define ALLOC_THRESHOLD_INDEX 64u
86
87
#define G_DATALIST_CLEAN_POINTER(ptr) \
88
38.9k
  ((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
G_ALWAYS_INLINE static inline GData *
155
g_datalist_lock_and_get (GData **datalist)
156
38.9k
{
157
38.9k
  guintptr ptr;
158
159
38.9k
  g_pointer_bit_lock_and_get ((void **) datalist, _G_DATALIST_LOCK_BIT, &ptr);
160
38.9k
  return G_DATALIST_CLEAN_POINTER (ptr);
161
38.9k
}
162
163
static void
164
g_datalist_unlock_and_set (GData **datalist, gpointer ptr)
165
12.9k
{
166
12.9k
  g_pointer_bit_unlock_and_set ((void **) datalist, _G_DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL);
167
12.9k
}
168
169
static gsize
170
datalist_alloc_size (guint32 alloc)
171
6.48k
{
172
  /* GDataElt also contains pointer. It thus is suitable aligned for pointers,
173
   * and we can just append the pointer for the index at the end. */
174
6.48k
  return G_STRUCT_OFFSET (GData, data) +
175
6.48k
         (((gsize) alloc) * sizeof (GDataElt)) +
176
6.48k
         (G_UNLIKELY (alloc >= ALLOC_THRESHOLD_INDEX) ? sizeof (GHashTable *) : 0u);
177
6.48k
}
178
179
G_ALWAYS_INLINE static inline GHashTable **
180
datalist_index_get_ptr (GData *data)
181
38.9k
{
182
38.9k
  if (G_LIKELY (data->alloc < ALLOC_THRESHOLD_INDEX))
183
38.9k
    return NULL;
184
185
0
  return (gpointer) (&(data->data[data->alloc]));
186
38.9k
}
187
188
G_ALWAYS_INLINE static inline GHashTable *
189
datalist_index_get (GData *data)
190
38.9k
{
191
38.9k
  GHashTable **p_index;
192
193
38.9k
  p_index = datalist_index_get_ptr (data);
194
195
38.9k
#if G_ENABLE_DEBUG
196
38.9k
  g_assert (!p_index || *p_index);
197
38.9k
#endif
198
199
38.9k
  return G_UNLIKELY (p_index) ? *p_index : NULL;
200
38.9k
}
201
202
static guint
203
_datalist_index_hash (gconstpointer key)
204
0
{
205
0
  const GQuark *ptr = key;
206
207
0
  G_STATIC_ASSERT (G_STRUCT_OFFSET (GDataElt, key) == 0);
208
209
0
  return *ptr;
210
0
}
211
212
static gboolean
213
_datalist_index_equal (gconstpointer a, gconstpointer b)
214
0
{
215
0
  const GQuark *ptr_a = a;
216
0
  const GQuark *ptr_b = b;
217
218
0
  return *ptr_a == *ptr_b;
219
0
}
220
221
G_ALWAYS_INLINE static inline GHashTable *
222
datalist_index_new (void)
223
0
{
224
0
  return g_hash_table_new (_datalist_index_hash, _datalist_index_equal);
225
0
}
226
227
static GData *
228
datalist_realloc (GData *data, guint32 alloc, gboolean *out_reallocated)
229
0
{
230
0
  guintptr data_old;
231
0
  gboolean reallocated;
232
0
  GHashTable *index;
233
0
  GHashTable **p_index;
234
0
  guint32 i;
235
236
0
  data_old = (guintptr) ((gpointer) data);
237
0
  index = datalist_index_get (data);
238
239
0
  data = g_realloc (data, datalist_alloc_size (alloc));
240
241
  /* Determine whether realloc() moves the pointer. After a move, the old
242
   * pointer would be dangling and comparing it would be undefined behavior.
243
   * Avoid that by casting to uintptr_t.
244
   */
245
0
  reallocated = (((guintptr) ((gpointer) (data))) != data_old);
246
247
0
  data->alloc = alloc;
248
249
0
  if (out_reallocated)
250
0
    *out_reallocated = reallocated;
251
252
  /* Note that if data was @reallocated, then @index contains only dangling pointers.
253
   * We can only destroy/remove-all, which we rely on not following those pointers. */
254
255
0
  p_index = datalist_index_get_ptr (data);
256
257
0
  if (G_LIKELY (!p_index))
258
0
    {
259
0
      if (G_UNLIKELY (index))
260
0
        g_hash_table_unref (index);
261
0
    }
262
0
  else if (!reallocated && index)
263
0
    {
264
      /* The index is still fine and the pointers are all still valid. We
265
       * can keep it. */
266
0
      *p_index = index;
267
0
    }
268
0
  else
269
0
    {
270
0
      if (G_UNLIKELY (index))
271
0
        {
272
          /* Note that the GHashTable's keys are now all dangling pointers!
273
           * We rely on remove-all to not following them. */
274
0
          g_hash_table_remove_all (index);
275
0
        }
276
0
      else
277
0
        index = datalist_index_new ();
278
279
0
      *p_index = index;
280
281
0
      for (i = 0; i < data->len; i++)
282
0
        g_hash_table_add (index, &data->data[i]);
283
0
    }
284
285
0
  return data;
286
0
}
287
288
static gboolean
289
datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
290
6.48k
{
291
6.48k
  GDataElt *data_elt;
292
6.48k
  GHashTable *index;
293
6.48k
  gboolean reallocated;
294
6.48k
  GData *d;
295
296
6.48k
#ifdef G_ENABLE_DEBUG
297
6.48k
  g_assert (key_id != 0);
298
6.48k
#endif
299
300
6.48k
  d = *data;
301
6.48k
  if (!d)
302
6.48k
    {
303
6.48k
      d = g_malloc (datalist_alloc_size (2u));
304
6.48k
      d->len = 0;
305
6.48k
      d->alloc = 2u;
306
307
6.48k
      if (2u >= ALLOC_THRESHOLD_INDEX)
308
0
        *(datalist_index_get_ptr (d)) = datalist_index_new ();
309
310
6.48k
      *data = d;
311
6.48k
      reallocated = TRUE;
312
6.48k
    }
313
0
  else if (d->len == d->alloc)
314
0
    {
315
0
      guint32 alloc = d->alloc * 2u;
316
317
0
      if (G_UNLIKELY (alloc < d->alloc))
318
0
        {
319
0
          if (d->alloc == G_MAXUINT32)
320
0
            g_error ("GData cannot contain more than 4294967295 entries");
321
0
          alloc = G_MAXUINT32;
322
0
        }
323
0
      d = datalist_realloc (d, alloc, &reallocated);
324
0
      *data = d;
325
0
    }
326
0
  else
327
0
    reallocated = FALSE;
328
329
6.48k
  data_elt = &d->data[d->len];
330
6.48k
  *data_elt = (GDataElt){
331
6.48k
    .key = key_id,
332
6.48k
    .data = new_data,
333
6.48k
    .destroy = destroy_func,
334
6.48k
  };
335
6.48k
  d->len++;
336
337
6.48k
  index = datalist_index_get (d);
338
6.48k
  if (G_UNLIKELY (index))
339
0
    g_hash_table_add (index, data_elt);
340
341
6.48k
  return reallocated;
342
6.48k
}
343
344
static void
345
datalist_remove (GData *data, guint32 idx)
346
0
{
347
0
  GHashTable *index;
348
349
0
#if G_ENABLE_DEBUG
350
0
  g_assert (idx < data->len);
351
0
#endif
352
353
  /* We remove the element similar to g_array_remove_index_fast(). That is, the
354
   * entries up to @idx are left unchanged, and the last entry is moved to
355
   * position @idx.
356
   */
357
358
0
  index = datalist_index_get (data);
359
0
  if (G_UNLIKELY (index))
360
0
    g_hash_table_remove (index, &data->data[idx]);
361
362
0
  data->len--;
363
364
0
  if (idx != data->len)
365
0
    {
366
0
      data->data[idx] = data->data[data->len];
367
0
      if (G_UNLIKELY (index))
368
0
        g_hash_table_add (index, &data->data[idx]);
369
0
    }
370
0
}
371
372
static gboolean
373
datalist_shrink (GData **data, GData **d_to_free)
374
0
{
375
0
  gboolean reallocated;
376
0
  guint32 alloc_by_4;
377
0
  guint32 v;
378
0
  GData *d;
379
380
0
  d = *data;
381
382
0
  alloc_by_4 = d->alloc / 4u;
383
384
0
  if (G_LIKELY (d->len > alloc_by_4))
385
0
    {
386
      /* No shrinking */
387
0
      return FALSE;
388
0
    }
389
390
0
  if (d->len == 0)
391
0
    {
392
0
      GHashTable *index;
393
394
      /* The list became empty. We drop the allocated memory altogether. */
395
396
      /* The caller will free the buffer after releasing the lock, to minimize
397
       * the time we hold the lock. Transfer it out. */
398
399
0
      index = datalist_index_get (d);
400
0
      if (G_UNLIKELY (index))
401
0
        g_hash_table_unref (index);
402
403
0
      *d_to_free = d;
404
0
      *data = NULL;
405
0
      return TRUE;
406
0
    }
407
408
  /* If the buffer is filled not more than 25%. Shrink to double the current length. */
409
410
0
  v = d->len;
411
0
  if (v != alloc_by_4)
412
0
    {
413
      /* d->alloc is a power of two (unless it's G_MAXUINT32). Usually, we
414
       * remove one element at a time, then we will just reach reach a quarter
415
       * of that.
416
       *
417
       * However, with g_datalist_id_remove_multiple(), len can be smaller
418
       * at once. In that case, find first the next power of two. */
419
0
      v = g_nearest_pow (v);
420
0
    }
421
0
  v *= 2u;
422
423
0
#if G_ENABLE_DEBUG
424
0
  g_assert (v > d->len);
425
0
  g_assert (v <= (d->alloc == G_MAXUINT32 ? 0x80000000u : d->alloc / 2u));
426
0
#endif
427
428
0
  d = datalist_realloc (d, v, &reallocated);
429
0
  *d_to_free = NULL;
430
0
  *data = d;
431
0
  return reallocated;
432
0
}
433
434
static void
435
datalist_destroy (GData *data)
436
6.48k
{
437
6.48k
  GHashTable *index;
438
6.48k
  guint32 i;
439
440
  /* Must be called without lock. Will free @data and invoke the
441
   * destroy() notifications. */
442
443
6.48k
  index = datalist_index_get (data);
444
6.48k
  if (G_UNLIKELY (index))
445
0
    g_hash_table_unref (index);
446
447
12.9k
  for (i = 0; i < data->len; i++)
448
6.48k
    {
449
6.48k
      if (data->data[i].destroy)
450
0
        data->data[i].destroy (data->data[i].data);
451
6.48k
    }
452
453
6.48k
  g_free (data);
454
6.48k
}
455
456
static GDataElt *
457
datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
458
32.4k
{
459
32.4k
  GDataElt *data_elt;
460
32.4k
  GHashTable *index;
461
32.4k
  guint32 i;
462
463
32.4k
  if (G_UNLIKELY (!data))
464
6.48k
    return NULL;
465
466
25.9k
  index = datalist_index_get (data);
467
468
25.9k
  if (G_LIKELY (!index))
469
25.9k
    {
470
      /* We have no index. Do a linear search. */
471
51.9k
      for (i = 0; i < data->len; i++)
472
25.9k
        {
473
25.9k
          data_elt = &data->data[i];
474
25.9k
          if (data_elt->key == key_id)
475
0
            {
476
0
              if (out_idx)
477
0
                *out_idx = i;
478
0
              return data_elt;
479
0
            }
480
25.9k
        }
481
482
25.9k
      return NULL;
483
25.9k
    }
484
485
0
  data_elt = g_hash_table_lookup (index, &key_id);
486
0
  if (!data_elt)
487
0
    return NULL;
488
489
0
#if G_ENABLE_DEBUG
490
0
  g_assert (data_elt >= data->data && data_elt < &data->data[data->len]);
491
0
#endif
492
493
0
  if (out_idx)
494
0
    *out_idx = (data_elt - data->data);
495
0
  return data_elt;
496
0
}
497
498
/**
499
 * g_datalist_clear: (skip)
500
 * @datalist: a datalist.
501
 *
502
 * Frees all the data elements of the datalist.
503
 * The data elements' destroy functions are called
504
 * if they have been set.
505
 **/
506
void
507
g_datalist_clear (GData **datalist)
508
6.48k
{
509
6.48k
  GData *data;
510
511
6.48k
  g_return_if_fail (datalist != NULL);
512
513
6.48k
  data = g_datalist_lock_and_get (datalist);
514
515
6.48k
  if (!data)
516
0
    {
517
0
      g_datalist_unlock (datalist);
518
0
      return;
519
0
    }
520
521
6.48k
  g_datalist_unlock_and_set (datalist, NULL);
522
523
6.48k
  datalist_destroy (data);
524
6.48k
}
525
526
/* HOLDS: g_dataset_global_lock */
527
static inline GDataset*
528
g_dataset_lookup (gconstpointer dataset_location)
529
0
{
530
0
  GDataset *dataset;
531
  
532
0
  if (g_dataset_cached && g_dataset_cached->location == dataset_location)
533
0
    return g_dataset_cached;
534
  
535
0
  dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
536
0
  if (dataset)
537
0
    g_dataset_cached = dataset;
538
  
539
0
  return dataset;
540
0
}
541
542
/* HOLDS: g_dataset_global_lock */
543
static void
544
g_dataset_destroy_internal (GDataset *dataset)
545
0
{
546
0
  gconstpointer dataset_location;
547
  
548
0
  dataset_location = dataset->location;
549
0
  while (dataset)
550
0
    {
551
0
      GData *data;
552
553
0
      data = G_DATALIST_GET_POINTER (&dataset->datalist);
554
555
0
      if (!data)
556
0
  {
557
0
    if (dataset == g_dataset_cached)
558
0
      g_dataset_cached = NULL;
559
0
    g_hash_table_remove (g_dataset_location_ht, dataset_location);
560
0
    g_slice_free (GDataset, dataset);
561
0
    break;
562
0
  }
563
564
0
      G_DATALIST_SET_POINTER (&dataset->datalist, NULL);
565
566
0
      G_UNLOCK (g_dataset_global);
567
568
0
      datalist_destroy (data);
569
570
0
      G_LOCK (g_dataset_global);
571
0
      dataset = g_dataset_lookup (dataset_location);
572
0
    }
573
0
}
574
575
/**
576
 * g_dataset_destroy:
577
 * @dataset_location: (not nullable): the location identifying the dataset.
578
 *
579
 * Destroys the dataset, freeing all memory allocated, and calling any
580
 * destroy functions set for data elements.
581
 */
582
void
583
g_dataset_destroy (gconstpointer  dataset_location)
584
0
{
585
0
  g_return_if_fail (dataset_location != NULL);
586
  
587
0
  G_LOCK (g_dataset_global);
588
0
  if (g_dataset_location_ht)
589
0
    {
590
0
      GDataset *dataset;
591
592
0
      dataset = g_dataset_lookup (dataset_location);
593
0
      if (dataset)
594
0
  g_dataset_destroy_internal (dataset);
595
0
    }
596
0
  G_UNLOCK (g_dataset_global);
597
0
}
598
599
/* HOLDS: g_dataset_global_lock if dataset != null */
600
static inline gpointer
601
g_data_set_internal (GData    **datalist,
602
         GQuark         key_id,
603
         gpointer       new_data,
604
         GDestroyNotify new_destroy_func,
605
         GDataset    *dataset)
606
0
{
607
0
  GData *d;
608
0
  GData *new_d = NULL;
609
0
  GDataElt old, *data;
610
0
  guint32 idx;
611
612
0
#ifdef G_ENABLE_DEBUG
613
0
  g_assert (key_id != 0);
614
0
#endif
615
616
0
  d = g_datalist_lock_and_get (datalist);
617
618
0
  data = datalist_find (d, key_id, &idx);
619
620
0
  if (new_data == NULL) /* remove */
621
0
    {
622
0
      if (data)
623
0
        {
624
0
          GData *d_to_free;
625
626
0
          old = *data;
627
628
0
          datalist_remove (d, idx);
629
0
          if (datalist_shrink (&d, &d_to_free))
630
0
            {
631
0
              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
0
              if (dataset && !d)
637
0
                g_dataset_destroy_internal (dataset);
638
639
0
              if (G_UNLIKELY (d_to_free))
640
0
                g_free (d_to_free);
641
0
            }
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
0
          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
0
          return old.data;
663
0
        }
664
0
    }
665
0
  else
666
0
    {
667
0
      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
0
      if (datalist_append (&d, key_id, new_data, new_destroy_func))
698
0
        new_d = d;
699
0
    }
700
701
0
  if (new_d)
702
0
    g_datalist_unlock_and_set (datalist, new_d);
703
0
  else
704
0
    g_datalist_unlock (datalist);
705
706
0
  return NULL;
707
708
0
}
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 (G_UNLIKELY (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
0
{
972
0
  g_return_if_fail (datalist != NULL);
973
0
  if (!data)
974
0
    g_return_if_fail (destroy_func == NULL);
975
0
  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
0
  g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
984
0
}
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
  if (key_id == 0)
1013
0
    return NULL;
1014
1015
0
  G_LOCK (g_dataset_global);
1016
0
  if (g_dataset_location_ht)
1017
0
    {
1018
0
      GDataset *dataset;
1019
  
1020
0
      dataset = g_dataset_lookup (dataset_location);
1021
0
      if (dataset)
1022
0
  ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
1023
0
    } 
1024
0
  G_UNLOCK (g_dataset_global);
1025
1026
0
  return ret_data;
1027
0
}
1028
1029
/**
1030
 * g_datalist_id_remove_no_notify: (skip)
1031
 * @datalist: a datalist.
1032
 * @key_id: the #GQuark identifying a data element.
1033
 *
1034
 * Removes an element, without calling its destroy notification
1035
 * function.
1036
 *
1037
 * Returns: (nullable): the data previously stored at @key_id,
1038
 *          or %NULL if none.
1039
 **/
1040
/**
1041
 * g_datalist_remove_no_notify: (skip)
1042
 * @dl: a datalist.
1043
 * @k: the string identifying the data element.
1044
 *
1045
 * Removes an element, without calling its destroy notifier.
1046
 **/
1047
gpointer
1048
g_datalist_id_remove_no_notify (GData **datalist,
1049
        GQuark    key_id)
1050
0
{
1051
0
  gpointer ret_data = NULL;
1052
1053
0
  g_return_val_if_fail (datalist != NULL, NULL);
1054
1055
0
  if (key_id)
1056
0
    ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
1057
1058
0
  return ret_data;
1059
0
}
1060
1061
/*< private >
1062
 * g_datalist_id_update_atomic:
1063
 * @datalist: the data list
1064
 * @key_id: the key to add.
1065
 * @already_locked: whether the GData lock is already held.
1066
 * @callback: (scope call): callback to update (set, remove, steal, update) the
1067
 *   data.
1068
 * @user_data: the user data for @callback.
1069
 *
1070
 * Will call @callback while holding the lock on @datalist. Be careful to not
1071
 * end up calling into another data-list function, because the lock is not
1072
 * reentrant and deadlock will happen.
1073
 *
1074
 * The callback receives the current data and destroy function. If @key_id is
1075
 * currently not in @datalist, they will be %NULL. The callback can update
1076
 * those pointers, and @datalist will be updated with the result. Note that if
1077
 * callback modifies a received data, then it MUST steal it and take ownership
1078
 * on it. Possibly by freeing it with the provided destroy function.
1079
 *
1080
 * The point is to atomically access the entry, while holding a lock
1081
 * of @datalist. Without this, the user would have to hold their own mutex
1082
 * while handling @key_id entry.
1083
 *
1084
 * The return value of @callback is not used, except it becomes the return
1085
 * value of the function. This is an alternative to returning a result via
1086
 * @user_data.
1087
 *
1088
 * If @already_locked is TRUE, the caller previously already called
1089
 * g_datalist_lock(). In that case, g_datalist_id_update_atomic() assumes it
1090
 * already holds the lock and does not take the lock again. Note that in any
1091
 * case, at the end g_datalist_id_update_atomic() will always unlock the GData.
1092
 * This asymmetry is here, because update may reallocate the buffer and it is
1093
 * more efficient to do when releasing the lock. The few callers that set
1094
 * @already_locked to TRUE are fine with this asymmetry and anyway want to
1095
 * unlock afterwards.
1096
 *
1097
 * Returns: the value returned by @callback.
1098
 */
1099
gpointer
1100
g_datalist_id_update_atomic (GData **datalist,
1101
                             GQuark key_id,
1102
                             gboolean already_locked,
1103
                             GDataListUpdateAtomicFunc callback,
1104
                             gpointer user_data)
1105
32.4k
{
1106
32.4k
  GData *d;
1107
32.4k
  GDataElt *data;
1108
32.4k
  gpointer new_data;
1109
32.4k
  gpointer result;
1110
32.4k
  GDestroyNotify new_destroy;
1111
32.4k
  guint32 idx;
1112
1113
32.4k
  g_return_val_if_fail (datalist, NULL);
1114
32.4k
  g_return_val_if_fail (key_id != 0, NULL);
1115
1116
32.4k
  if (G_UNLIKELY (already_locked))
1117
0
    {
1118
0
      d = G_DATALIST_GET_POINTER (datalist);
1119
0
    }
1120
32.4k
  else
1121
32.4k
    {
1122
32.4k
      d = g_datalist_lock_and_get (datalist);
1123
32.4k
    }
1124
1125
32.4k
  data = datalist_find (d, key_id, &idx);
1126
1127
32.4k
  if (data)
1128
0
    {
1129
0
      new_data = data->data;
1130
0
      new_destroy = data->destroy;
1131
0
    }
1132
32.4k
  else
1133
32.4k
    {
1134
32.4k
      new_data = NULL;
1135
32.4k
      new_destroy = NULL;
1136
32.4k
    }
1137
1138
32.4k
  result = callback (&new_data, &new_destroy, user_data);
1139
1140
32.4k
  if (G_LIKELY (data))
1141
0
    {
1142
0
      if (G_LIKELY (data->data == new_data && data->destroy == new_destroy))
1143
0
        {
1144
          /* No change. */
1145
0
        }
1146
0
      else if (!new_data)
1147
0
        {
1148
0
          GData *d_to_free;
1149
1150
          /* Remove. The callback indicates to drop the entry.
1151
           *
1152
           * The old data->data was stolen by callback(). */
1153
0
          datalist_remove (d, idx);
1154
0
          if (datalist_shrink (&d, &d_to_free))
1155
0
            {
1156
0
              g_datalist_unlock_and_set (datalist, d);
1157
0
              if (G_UNLIKELY (d_to_free))
1158
0
                g_free (d_to_free);
1159
0
              goto return_without_unlock;
1160
0
            }
1161
0
        }
1162
0
      else
1163
0
        {
1164
          /* Update. The callback may have provided new pointers to an existing
1165
           * entry.
1166
           *
1167
           * The old data was stolen by callback(). We only update the pointers and
1168
           * are done. */
1169
0
          data->data = new_data;
1170
0
          data->destroy = new_destroy;
1171
0
        }
1172
0
    }
1173
32.4k
  else
1174
32.4k
    {
1175
32.4k
      if (G_LIKELY (!new_data))
1176
25.9k
        {
1177
          /* No change. The entry didn't exist and still does not. */
1178
25.9k
        }
1179
6.48k
      else
1180
6.48k
        {
1181
          /* Add. Add a new entry that didn't exist previously. */
1182
6.48k
          if (datalist_append (&d, key_id, new_data, new_destroy))
1183
6.48k
            {
1184
6.48k
              g_datalist_unlock_and_set (datalist, d);
1185
6.48k
              goto return_without_unlock;
1186
6.48k
            }
1187
6.48k
        }
1188
32.4k
    }
1189
1190
32.4k
  g_datalist_unlock (datalist);
1191
1192
32.4k
return_without_unlock:
1193
32.4k
  return result;
1194
25.9k
}
1195
1196
/**
1197
 * g_dataset_id_get_data:
1198
 * @dataset_location: (not nullable): the location identifying the dataset.
1199
 * @key_id: the #GQuark id to identify the data element.
1200
 *
1201
 * Gets the data element corresponding to a #GQuark.
1202
 *
1203
 * Returns: (transfer none) (nullable): the data element corresponding to
1204
 *          the #GQuark, or %NULL if it is not found.
1205
 **/
1206
/**
1207
 * g_dataset_get_data:
1208
 * @l: the location identifying the dataset.
1209
 * @k: the string identifying the data element.
1210
 *
1211
 * Gets the data element corresponding to a string.
1212
 *
1213
 * Returns: (transfer none) (nullable): the data element corresponding to
1214
 *          the string, or %NULL if it is not found.
1215
 **/
1216
gpointer
1217
g_dataset_id_get_data (gconstpointer  dataset_location,
1218
           GQuark         key_id)
1219
0
{
1220
0
  gpointer retval = NULL;
1221
1222
0
  g_return_val_if_fail (dataset_location != NULL, NULL);
1223
1224
0
  if (key_id == 0)
1225
0
    return NULL;
1226
1227
0
  G_LOCK (g_dataset_global);
1228
0
  if (g_dataset_location_ht)
1229
0
    {
1230
0
      GDataset *dataset;
1231
      
1232
0
      dataset = g_dataset_lookup (dataset_location);
1233
0
      if (dataset)
1234
0
  retval = g_datalist_id_get_data (&dataset->datalist, key_id);
1235
0
    }
1236
0
  G_UNLOCK (g_dataset_global);
1237
 
1238
0
  return retval;
1239
0
}
1240
1241
/**
1242
 * g_datalist_id_get_data:
1243
 * @datalist: a datalist.
1244
 * @key_id: the #GQuark identifying a data element.
1245
 *
1246
 * Retrieves the data element corresponding to @key_id.
1247
 *
1248
 * Returns: (transfer none) (nullable): the data element, or %NULL if
1249
 *          it is not found.
1250
 */
1251
gpointer
1252
g_datalist_id_get_data (GData  **datalist,
1253
      GQuark   key_id)
1254
0
{
1255
0
  return g_datalist_id_dup_data (datalist, key_id, NULL, NULL);
1256
0
}
1257
1258
/**
1259
 * GDuplicateFunc:
1260
 * @data: the data to duplicate
1261
 * @user_data: (closure): user data that was specified in
1262
 *             g_datalist_id_dup_data()
1263
 *
1264
 * The type of functions that are used to 'duplicate' an object.
1265
 * What this means depends on the context, it could just be
1266
 * incrementing the reference count, if @data is a ref-counted
1267
 * object.
1268
 *
1269
 * Returns: a duplicate of data
1270
 */
1271
1272
/**
1273
 * g_datalist_id_dup_data: (skip)
1274
 * @datalist: location of a datalist
1275
 * @key_id: the #GQuark identifying a data element
1276
 * @dup_func: (scope call) (closure user_data) (nullable): function to
1277
 *   duplicate the old value
1278
 * @user_data: passed as user_data to @dup_func
1279
 *
1280
 * This is a variant of g_datalist_id_get_data() which
1281
 * returns a 'duplicate' of the value. @dup_func defines the
1282
 * meaning of 'duplicate' in this context, it could e.g.
1283
 * take a reference on a ref-counted object.
1284
 *
1285
 * If the @key_id is not set in the datalist then @dup_func
1286
 * will be called with a %NULL argument.
1287
 *
1288
 * Note that @dup_func is called while the datalist is locked, so it
1289
 * is not allowed to read or modify the datalist.
1290
 *
1291
 * This function can be useful to avoid races when multiple
1292
 * threads are using the same datalist and the same key.
1293
 *
1294
 * Returns: (nullable): the result of calling @dup_func on the value
1295
 *     associated with @key_id in @datalist, or %NULL if not set.
1296
 *     If @dup_func is %NULL, the value is returned unmodified.
1297
 *
1298
 * Since: 2.34
1299
 */
1300
gpointer
1301
g_datalist_id_dup_data (GData          **datalist,
1302
                        GQuark           key_id,
1303
                        GDuplicateFunc   dup_func,
1304
                        gpointer         user_data)
1305
0
{
1306
0
  gpointer val = NULL;
1307
0
  gpointer retval = NULL;
1308
0
  GData *d;
1309
0
  GDataElt *data;
1310
1311
0
  d = g_datalist_lock_and_get (datalist);
1312
1313
0
  data = datalist_find (d, key_id, NULL);
1314
0
  if (data)
1315
0
    val = data->data;
1316
1317
0
  if (dup_func)
1318
0
    retval = dup_func (val, user_data);
1319
0
  else
1320
0
    retval = val;
1321
1322
0
  g_datalist_unlock (datalist);
1323
1324
0
  return retval;
1325
0
}
1326
1327
/**
1328
 * g_datalist_id_replace_data: (skip)
1329
 * @datalist: location of a datalist
1330
 * @key_id: the #GQuark identifying a data element
1331
 * @oldval: (nullable): the old value to compare against
1332
 * @newval: (nullable): the new value to replace it with
1333
 * @destroy: (nullable): destroy notify for the new value
1334
 * @old_destroy: (out) (optional): destroy notify for the existing value
1335
 *
1336
 * Compares the member that is associated with @key_id in
1337
 * @datalist to @oldval, and if they are the same, replace
1338
 * @oldval with @newval.
1339
 *
1340
 * This is like a typical atomic compare-and-exchange
1341
 * operation, for a member of @datalist.
1342
 *
1343
 * If the previous value was replaced then ownership of the
1344
 * old value (@oldval) is passed to the caller, including
1345
 * the registered destroy notify for it (passed out in @old_destroy).
1346
 * Its up to the caller to free this as they wish, which may
1347
 * or may not include using @old_destroy as sometimes replacement
1348
 * should not destroy the object in the normal way.
1349
 *
1350
 * Returns: %TRUE if the existing value for @key_id was replaced
1351
 *  by @newval, %FALSE otherwise.
1352
 *
1353
 * Since: 2.34
1354
 */
1355
gboolean
1356
g_datalist_id_replace_data (GData          **datalist,
1357
                            GQuark           key_id,
1358
                            gpointer         oldval,
1359
                            gpointer         newval,
1360
                            GDestroyNotify   destroy,
1361
                            GDestroyNotify  *old_destroy)
1362
0
{
1363
0
  gpointer val = NULL;
1364
0
  GData *d;
1365
0
  GDataElt *data;
1366
0
  GData *d_to_free = NULL;
1367
0
  gboolean set_d = FALSE;
1368
0
  guint32 idx;
1369
1370
0
  g_return_val_if_fail (datalist != NULL, FALSE);
1371
0
  g_return_val_if_fail (key_id != 0, FALSE);
1372
1373
0
  if (old_destroy)
1374
0
    *old_destroy = NULL;
1375
1376
0
  d = g_datalist_lock_and_get (datalist);
1377
1378
0
  data = datalist_find (d, key_id, &idx);
1379
0
  if (data)
1380
0
    {
1381
0
      val = data->data;
1382
0
      if (val == oldval)
1383
0
        {
1384
0
          if (old_destroy)
1385
0
            *old_destroy = data->destroy;
1386
0
          if (newval != NULL)
1387
0
            {
1388
0
              data->data = newval;
1389
0
              data->destroy = destroy;
1390
0
            }
1391
0
          else
1392
0
            {
1393
0
              datalist_remove (d, idx);
1394
0
              if (datalist_shrink (&d, &d_to_free))
1395
0
                set_d = TRUE;
1396
0
            }
1397
0
        }
1398
0
    }
1399
0
  else
1400
0
    {
1401
0
      if (oldval == NULL && newval != NULL)
1402
0
        {
1403
0
          if (datalist_append (&d, key_id, newval, destroy))
1404
0
            set_d = TRUE;
1405
0
        }
1406
0
    }
1407
1408
0
  if (set_d)
1409
0
    g_datalist_unlock_and_set (datalist, d);
1410
0
  else
1411
0
    g_datalist_unlock (datalist);
1412
1413
0
  if (G_UNLIKELY (d_to_free))
1414
0
    g_free (d_to_free);
1415
1416
0
  return val == oldval;
1417
0
}
1418
1419
/**
1420
 * g_datalist_get_data:
1421
 * @datalist: a datalist.
1422
 * @key: the string identifying a data element.
1423
 *
1424
 * Gets a data element, using its string identifier. This is slower than
1425
 * g_datalist_id_get_data() because it compares strings.
1426
 *
1427
 * Returns: (transfer none) (nullable): the data element, or %NULL if it
1428
 *          is not found.
1429
 **/
1430
gpointer
1431
g_datalist_get_data (GData **datalist,
1432
                     const gchar *key)
1433
0
{
1434
0
  GQuark key_id;
1435
0
  GHashTable *index;
1436
0
  gpointer res = NULL;
1437
0
  GDataElt *data_elt;
1438
0
  GData *d;
1439
1440
0
  g_return_val_if_fail (datalist != NULL, NULL);
1441
1442
0
  if (G_UNLIKELY (!key))
1443
0
    return NULL;
1444
1445
0
  d = g_datalist_lock_and_get (datalist);
1446
1447
0
  if (!d)
1448
0
    goto out;
1449
1450
0
  index = datalist_index_get (d);
1451
1452
0
  if (G_LIKELY (!index))
1453
0
    {
1454
0
      guint32 i;
1455
1456
0
      for (i = 0; i < d->len; i++)
1457
0
        {
1458
0
          const char *qstr;
1459
1460
0
          data_elt = &d->data[i];
1461
          /* Here we intentionally compare by strings, instead of calling
1462
           * g_quark_try_string() first.
1463
           *
1464
           * See commit 1cceda49b60b ('Make g_datalist_get_data not look up the
1465
           * quark').
1466
           */
1467
0
          qstr = g_quark_to_string (data_elt->key);
1468
0
          if (qstr && strcmp (qstr, key) == 0)
1469
0
            {
1470
0
              res = data_elt->data;
1471
0
              goto out;
1472
0
            }
1473
0
        }
1474
0
      goto out;
1475
0
    }
1476
1477
0
  key_id = g_quark_try_string (key);
1478
0
  if (key_id == 0)
1479
0
    goto out;
1480
1481
0
  data_elt = g_hash_table_lookup (index, &key_id);
1482
1483
0
  if (data_elt)
1484
0
    res = data_elt->data;
1485
1486
0
out:
1487
0
  g_datalist_unlock (datalist);
1488
1489
0
  return res;
1490
0
}
1491
1492
/**
1493
 * GDataForeachFunc:
1494
 * @key_id: the #GQuark id to identifying the data element.
1495
 * @data: the data element.
1496
 * @user_data: (closure): user data passed to g_dataset_foreach().
1497
 *
1498
 * Specifies the type of function passed to g_dataset_foreach(). It is
1499
 * called with each #GQuark id and associated data element, together
1500
 * with the @user_data parameter supplied to g_dataset_foreach().
1501
 **/
1502
1503
/**
1504
 * g_dataset_foreach:
1505
 * @dataset_location: (not nullable): the location identifying the dataset.
1506
 * @func: (scope call) (closure user_data): the function to call for each data element.
1507
 * @user_data: user data to pass to the function.
1508
 *
1509
 * Calls the given function for each data element which is associated
1510
 * with the given location. Note that this function is NOT thread-safe.
1511
 * So unless @dataset_location can be protected from any modifications
1512
 * during invocation of this function, it should not be called.
1513
 *
1514
 * @func can make changes to the dataset, but the iteration will not
1515
 * reflect changes made during the g_dataset_foreach() call, other
1516
 * than skipping over elements that are removed.
1517
 **/
1518
void
1519
g_dataset_foreach (gconstpointer    dataset_location,
1520
       GDataForeachFunc func,
1521
       gpointer         user_data)
1522
0
{
1523
0
  GDataset *dataset;
1524
  
1525
0
  g_return_if_fail (dataset_location != NULL);
1526
0
  g_return_if_fail (func != NULL);
1527
1528
0
  G_LOCK (g_dataset_global);
1529
0
  if (g_dataset_location_ht)
1530
0
    {
1531
0
      dataset = g_dataset_lookup (dataset_location);
1532
0
      G_UNLOCK (g_dataset_global);
1533
0
      if (dataset)
1534
0
  g_datalist_foreach (&dataset->datalist, func, user_data);
1535
0
    }
1536
0
  else
1537
0
    {
1538
0
      G_UNLOCK (g_dataset_global);
1539
0
    }
1540
0
}
1541
1542
/**
1543
 * g_datalist_foreach:
1544
 * @datalist: a datalist.
1545
 * @func: (scope call) (closure user_data): the function to call for each data element.
1546
 * @user_data: user data to pass to the function.
1547
 *
1548
 * Calls the given function for each data element of the datalist. The
1549
 * function is called with each data element's #GQuark id and data,
1550
 * together with the given @user_data parameter. Note that this
1551
 * function is NOT thread-safe. So unless @datalist can be protected
1552
 * from any modifications during invocation of this function, it should
1553
 * not be called.
1554
 *
1555
 * @func can make changes to @datalist, but the iteration will not
1556
 * reflect changes made during the g_datalist_foreach() call, other
1557
 * than skipping over elements that are removed.
1558
 **/
1559
void
1560
g_datalist_foreach (GData    **datalist,
1561
        GDataForeachFunc func,
1562
        gpointer         user_data)
1563
0
{
1564
0
  GData *d;
1565
0
  guint i, j, len;
1566
0
  GQuark *keys;
1567
1568
0
  g_return_if_fail (datalist != NULL);
1569
0
  g_return_if_fail (func != NULL);
1570
1571
0
  d = G_DATALIST_GET_POINTER (datalist);
1572
0
  if (d == NULL) 
1573
0
    return;
1574
1575
  /* We make a copy of the keys so that we can handle it changing
1576
     in the callback */
1577
0
  len = d->len;
1578
0
  keys = g_new (GQuark, len);
1579
0
  for (i = 0; i < len; i++)
1580
0
    keys[i] = d->data[i].key;
1581
  
1582
0
  for (i = 0; i < len; i++)
1583
0
    {
1584
      /* A previous callback might have removed a later item, so always check that
1585
   it still exists before calling */
1586
0
      d = G_DATALIST_GET_POINTER (datalist);
1587
      
1588
0
      if (d == NULL)
1589
0
  break;
1590
0
      for (j = 0; j < d->len; j++)
1591
0
  {
1592
0
    if (d->data[j].key == keys[i]) {
1593
0
      func (d->data[i].key, d->data[i].data, user_data);
1594
0
      break;
1595
0
    }
1596
0
  }
1597
0
    }
1598
0
  g_free (keys);
1599
0
}
1600
1601
/**
1602
 * g_datalist_init: (skip)
1603
 * @datalist: a pointer to a pointer to a datalist.
1604
 *
1605
 * Resets the datalist to %NULL. It does not free any memory or call
1606
 * any destroy functions.
1607
 **/
1608
void
1609
g_datalist_init (GData **datalist)
1610
0
{
1611
0
  g_return_if_fail (datalist != NULL);
1612
1613
0
  g_atomic_pointer_set (datalist, NULL);
1614
0
}
1615
1616
/**
1617
 * g_datalist_set_flags:
1618
 * @datalist: pointer to the location that holds a list
1619
 * @flags: the flags to turn on. The values of the flags are
1620
 *   restricted by %G_DATALIST_FLAGS_MASK (currently
1621
 *   3; giving two possible boolean flags).
1622
 *   A value for @flags that doesn't fit within the mask is
1623
 *   an error.
1624
 * 
1625
 * Turns on flag values for a data list. This function is used
1626
 * to keep a small number of boolean flags in an object with
1627
 * a data list without using any additional space. It is
1628
 * not generally useful except in circumstances where space
1629
 * is very tight. (It is used in the base #GObject type, for
1630
 * example.)
1631
 *
1632
 * Since: 2.8
1633
 **/
1634
void
1635
g_datalist_set_flags (GData **datalist,
1636
          guint   flags)
1637
66
{
1638
66
  g_return_if_fail (datalist != NULL);
1639
66
  g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1640
1641
66
  g_atomic_pointer_or (datalist, (gsize)flags);
1642
66
}
1643
1644
/**
1645
 * g_datalist_unset_flags:
1646
 * @datalist: pointer to the location that holds a list
1647
 * @flags: the flags to turn off. The values of the flags are
1648
 *   restricted by %G_DATALIST_FLAGS_MASK (currently
1649
 *   3: giving two possible boolean flags).
1650
 *   A value for @flags that doesn't fit within the mask is
1651
 *   an error.
1652
 * 
1653
 * Turns off flag values for a data list. See g_datalist_unset_flags()
1654
 *
1655
 * Since: 2.8
1656
 **/
1657
void
1658
g_datalist_unset_flags (GData **datalist,
1659
      guint   flags)
1660
0
{
1661
0
  g_return_if_fail (datalist != NULL);
1662
0
  g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1663
1664
0
  g_atomic_pointer_and (datalist, ~(gsize)flags);
1665
0
}
1666
1667
/**
1668
 * g_datalist_get_flags:
1669
 * @datalist: pointer to the location that holds a list
1670
 * 
1671
 * Gets flags values packed in together with the datalist.
1672
 * See g_datalist_set_flags().
1673
 * 
1674
 * Returns: the flags of the datalist
1675
 *
1676
 * Since: 2.8
1677
 **/
1678
guint
1679
g_datalist_get_flags (GData **datalist)
1680
2.89k
{
1681
2.89k
  g_return_val_if_fail (datalist != NULL, 0);
1682
  
1683
2.89k
  return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
1684
2.89k
}
1685
1686
/* HOLDS: g_dataset_global_lock */
1687
static void
1688
g_data_initialize (void)
1689
0
{
1690
0
  g_return_if_fail (g_dataset_location_ht == NULL);
1691
1692
0
  g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
1693
0
  g_dataset_cached = NULL;
1694
0
}