Branch data Line data Source code
1 : : /* GLIB - Library of useful routines for C programming
2 : : * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 : : *
4 : : * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
5 : : * Copyright (C) 1998 Tim Janik
6 : : *
7 : : * SPDX-License-Identifier: LGPL-2.1-or-later
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General Public
20 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 : : */
22 : :
23 : : /*
24 : : * Modified by the GLib Team and others 1997-2000. See the AUTHORS
25 : : * file for a list of people on the GLib Team. See the ChangeLog
26 : : * files for a list of changes. These files are distributed with
27 : : * GLib at ftp://ftp.gtk.org/pub/gtk/.
28 : : */
29 : :
30 : : /*
31 : : * MT safe ; except for g_data*_foreach()
32 : : */
33 : :
34 : : #include "config.h"
35 : :
36 : : #include <string.h>
37 : :
38 : : #include "gdataset.h"
39 : : #include "gbitlock.h"
40 : :
41 : : #include "gslice.h"
42 : : #include "gdatasetprivate.h"
43 : : #include "gutilsprivate.h"
44 : : #include "ghash.h"
45 : : #include "gquark.h"
46 : : #include "gstrfuncs.h"
47 : : #include "gtestutils.h"
48 : : #include "gthread.h"
49 : : #include "glib_trace.h"
50 : : #include "galloca.h"
51 : :
52 : : /**
53 : : * GData:
54 : : *
55 : : * An opaque data structure that represents a keyed data list.
56 : : *
57 : : * See also: [Keyed data lists](datalist-and-dataset.html).
58 : : **/
59 : :
60 : : /**
61 : : * GDestroyNotify:
62 : : * @data: the data element.
63 : : *
64 : : * Specifies the type of function which is called when a data element
65 : : * is destroyed. It is passed the pointer to the data element and
66 : : * should free any memory and resources allocated for it.
67 : : **/
68 : :
69 : : #define G_DATALIST_FLAGS_MASK_INTERNAL 0x7
70 : :
71 : : /* 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 : : #define ALLOC_THRESHOLD_INDEX 64u
86 : :
87 : : #define G_DATALIST_CLEAN_POINTER(ptr) \
88 : : ((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 : : G_DATALIST_CLEAN_POINTER (g_atomic_pointer_get (datalist))
93 : :
94 : : #define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START { \
95 : : gpointer _oldv = g_atomic_pointer_get (datalist); \
96 : : gpointer _newv; \
97 : : do { \
98 : : _newv = (gpointer) (((guintptr) _oldv & ((guintptr) G_DATALIST_FLAGS_MASK_INTERNAL)) | (guintptr) pointer); \
99 : : } while (!g_atomic_pointer_compare_and_exchange_full ((void**) datalist, _oldv, \
100 : : _newv, &_oldv)); \
101 : : } 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 : : {
157 : : guintptr ptr;
158 : :
159 : 71220607 : g_pointer_bit_lock_and_get ((void **) datalist, _G_DATALIST_LOCK_BIT, &ptr);
160 : 71294988 : return G_DATALIST_CLEAN_POINTER (ptr);
161 : : }
162 : :
163 : : static void
164 : 21266185 : g_datalist_unlock_and_set (GData **datalist, gpointer ptr)
165 : : {
166 : 21266185 : g_pointer_bit_unlock_and_set ((void **) datalist, _G_DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL);
167 : 21266185 : }
168 : :
169 : : static gsize
170 : 10634023 : datalist_alloc_size (guint32 alloc)
171 : : {
172 : : /* GDataElt also contains pointer. It thus is suitably aligned for pointers,
173 : : * and we can just append the pointer for the index at the end. */
174 : : return G_STRUCT_OFFSET (GData, data) +
175 : 10634023 : (((gsize) alloc) * sizeof (GDataElt)) +
176 : 10634023 : (G_UNLIKELY (alloc >= ALLOC_THRESHOLD_INDEX) ? sizeof (GHashTable *) : 0u);
177 : : }
178 : :
179 : : G_ALWAYS_INLINE static inline GHashTable **
180 : : datalist_index_get_ptr (GData *data)
181 : : {
182 : 58331433 : if (G_LIKELY (data->alloc < ALLOC_THRESHOLD_INDEX))
183 : 77171304 : return NULL;
184 : :
185 : 20666 : return (gpointer) (&(data->data[data->alloc]));
186 : : }
187 : :
188 : : G_ALWAYS_INLINE static inline GHashTable *
189 : : datalist_index_get (GData *data)
190 : : {
191 : : GHashTable **p_index;
192 : :
193 : 77190623 : p_index = datalist_index_get_ptr (data);
194 : :
195 : : #if G_ENABLE_DEBUG
196 : 77190623 : g_assert (!p_index || *p_index);
197 : : #endif
198 : :
199 : 77190623 : return G_UNLIKELY (p_index) ? *p_index : NULL;
200 : : }
201 : :
202 : : static guint
203 : 25474 : _datalist_index_hash (gconstpointer key)
204 : : {
205 : 25474 : const GQuark *ptr = key;
206 : :
207 : : G_STATIC_ASSERT (G_STRUCT_OFFSET (GDataElt, key) == 0);
208 : :
209 : 25474 : return *ptr;
210 : : }
211 : :
212 : : static gboolean
213 : 13284 : _datalist_index_equal (gconstpointer a, gconstpointer b)
214 : : {
215 : 13284 : const GQuark *ptr_a = a;
216 : 13284 : const GQuark *ptr_b = b;
217 : :
218 : 13284 : return *ptr_a == *ptr_b;
219 : : }
220 : :
221 : : G_ALWAYS_INLINE static inline GHashTable *
222 : : datalist_index_new (void)
223 : : {
224 : 4 : return g_hash_table_new (_datalist_index_hash, _datalist_index_equal);
225 : : }
226 : :
227 : : static GData *
228 : 1347 : datalist_realloc (GData *data, guint32 alloc, gboolean *out_reallocated)
229 : : {
230 : : guintptr data_old;
231 : : gboolean reallocated;
232 : : GHashTable *index;
233 : : GHashTable **p_index;
234 : : guint32 i;
235 : :
236 : 1347 : data_old = (guintptr) ((gpointer) data);
237 : 1347 : index = datalist_index_get (data);
238 : :
239 : 1347 : 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 : 1347 : reallocated = (((guintptr) ((gpointer) (data))) != data_old);
246 : :
247 : 1347 : data->alloc = alloc;
248 : :
249 : 1347 : if (out_reallocated)
250 : 1347 : *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 : 1347 : p_index = datalist_index_get_ptr (data);
256 : :
257 : 1347 : if (G_LIKELY (!p_index))
258 : : {
259 : 1322 : if (G_UNLIKELY (index))
260 : 2 : g_hash_table_unref (index);
261 : : }
262 : 25 : else if (!reallocated && index)
263 : : {
264 : : /* The index is still fine and the pointers are all still valid. We
265 : : * can keep it. */
266 : 10 : *p_index = index;
267 : : }
268 : : else
269 : : {
270 : 15 : if (G_UNLIKELY (index))
271 : : {
272 : : /* Note that the GHashTable's keys are now all dangling pointers!
273 : : * We rely on remove-all to not following them. */
274 : 11 : g_hash_table_remove_all (index);
275 : : }
276 : : else
277 : 4 : index = datalist_index_new ();
278 : :
279 : 15 : *p_index = index;
280 : :
281 : 2319 : for (i = 0; i < data->len; i++)
282 : 2304 : g_hash_table_add (index, &data->data[i]);
283 : : }
284 : :
285 : 1347 : return data;
286 : : }
287 : :
288 : : static gboolean
289 : 10649660 : datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
290 : : {
291 : : GDataElt *data_elt;
292 : : GHashTable *index;
293 : : gboolean reallocated;
294 : : GData *d;
295 : :
296 : : #ifdef G_ENABLE_DEBUG
297 : 10649660 : g_assert (key_id != 0);
298 : : #endif
299 : :
300 : 10649660 : d = *data;
301 : 10649660 : if (!d)
302 : : {
303 : 10632676 : d = g_malloc (datalist_alloc_size (2u));
304 : 10632676 : d->len = 0;
305 : 10632676 : d->alloc = 2u;
306 : :
307 : : if (2u >= ALLOC_THRESHOLD_INDEX)
308 : : *(datalist_index_get_ptr (d)) = datalist_index_new ();
309 : :
310 : 10632676 : *data = d;
311 : 10632676 : reallocated = TRUE;
312 : : }
313 : 16984 : else if (d->len == d->alloc)
314 : : {
315 : 1293 : guint32 alloc = d->alloc * 2u;
316 : :
317 : 1293 : if (G_UNLIKELY (alloc < d->alloc))
318 : : {
319 : 0 : if (d->alloc == G_MAXUINT32)
320 : 0 : g_error ("GData cannot contain more than 4294967295 entries");
321 : 0 : alloc = G_MAXUINT32;
322 : : }
323 : 1293 : d = datalist_realloc (d, alloc, &reallocated);
324 : 1293 : *data = d;
325 : : }
326 : : else
327 : 15691 : reallocated = FALSE;
328 : :
329 : 10649660 : data_elt = &d->data[d->len];
330 : 10649660 : *data_elt = (GDataElt){
331 : : .key = key_id,
332 : : .data = new_data,
333 : : .destroy = destroy_func,
334 : : };
335 : 10649660 : d->len++;
336 : :
337 : 10649660 : index = datalist_index_get (d);
338 : 10649660 : if (G_UNLIKELY (index))
339 : 3350 : g_hash_table_add (index, data_elt);
340 : :
341 : 10649660 : return reallocated;
342 : : }
343 : :
344 : : static void
345 : 2426892 : datalist_remove (GData *data, guint32 idx)
346 : : {
347 : : GHashTable *index;
348 : :
349 : : #if G_ENABLE_DEBUG
350 : 2426892 : g_assert (idx < data->len);
351 : : #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 : 2426892 : index = datalist_index_get (data);
359 : 2426892 : if (G_UNLIKELY (index))
360 : 3244 : g_hash_table_remove (index, &data->data[idx]);
361 : :
362 : 2426892 : data->len--;
363 : :
364 : 2426892 : if (idx != data->len)
365 : : {
366 : 3045 : data->data[idx] = data->data[data->len];
367 : 3045 : if (G_UNLIKELY (index))
368 : 2554 : g_hash_table_add (index, &data->data[idx]);
369 : : }
370 : 2426892 : }
371 : :
372 : : static gboolean
373 : 2424834 : datalist_shrink (GData **data, GData **d_to_free)
374 : : {
375 : : gboolean reallocated;
376 : : guint32 alloc_by_4;
377 : : guint32 v;
378 : : GData *d;
379 : :
380 : 2424834 : d = *data;
381 : :
382 : 2424834 : alloc_by_4 = d->alloc / 4u;
383 : :
384 : 2424834 : if (G_LIKELY (d->len > alloc_by_4))
385 : : {
386 : : /* No shrinking */
387 : 2023 : return FALSE;
388 : : }
389 : :
390 : 2422811 : if (d->len == 0)
391 : : {
392 : : 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 : 2422757 : index = datalist_index_get (d);
400 : 2422757 : if (G_UNLIKELY (index))
401 : 1 : g_hash_table_unref (index);
402 : :
403 : 2422757 : *d_to_free = d;
404 : 2422757 : *data = NULL;
405 : 2422757 : return TRUE;
406 : : }
407 : :
408 : : /* If the buffer is filled not more than 25%. Shrink to double the current length. */
409 : :
410 : 54 : v = d->len;
411 : 54 : if (v != alloc_by_4)
412 : : {
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 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 : 2 : v = g_nearest_pow (v);
420 : : }
421 : 54 : v *= 2u;
422 : :
423 : : #if G_ENABLE_DEBUG
424 : 54 : g_assert (v > d->len);
425 : 54 : g_assert (v <= (d->alloc == G_MAXUINT32 ? 0x80000000u : d->alloc / 2u));
426 : : #endif
427 : :
428 : 54 : d = datalist_realloc (d, v, &reallocated);
429 : 54 : *d_to_free = NULL;
430 : 54 : *data = d;
431 : 54 : return reallocated;
432 : : }
433 : :
434 : : static void
435 : 8209530 : datalist_destroy (GData *data)
436 : : {
437 : : GHashTable *index;
438 : : guint32 i;
439 : :
440 : : /* Must be called without lock. Will free @data and invoke the
441 : : * destroy() notifications. */
442 : :
443 : 8209530 : index = datalist_index_get (data);
444 : 8209530 : if (G_UNLIKELY (index))
445 : 1 : g_hash_table_unref (index);
446 : :
447 : 16431906 : for (i = 0; i < data->len; i++)
448 : : {
449 : 8222376 : if (data->data[i].destroy)
450 : 10634 : data->data[i].destroy (data->data[i].data);
451 : : }
452 : :
453 : 8209530 : g_free (data);
454 : 8209530 : }
455 : :
456 : : static GDataElt *
457 : 64658896 : datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
458 : : {
459 : : GDataElt *data_elt;
460 : : GHashTable *index;
461 : : guint32 i;
462 : :
463 : 64658896 : if (G_UNLIKELY (!data))
464 : 11466517 : return NULL;
465 : :
466 : 53192379 : index = datalist_index_get (data);
467 : :
468 : 53192379 : if (G_LIKELY (!index))
469 : : {
470 : : /* We have no index. Do a linear search. */
471 : 86093560 : for (i = 0; i < data->len; i++)
472 : : {
473 : 53237655 : data_elt = &data->data[i];
474 : 53237655 : if (data_elt->key == key_id)
475 : : {
476 : 20323927 : if (out_idx)
477 : 20251853 : *out_idx = i;
478 : 20323927 : return data_elt;
479 : : }
480 : : }
481 : :
482 : 32855905 : return NULL;
483 : : }
484 : :
485 : 12547 : data_elt = g_hash_table_lookup (index, &key_id);
486 : 12547 : if (!data_elt)
487 : 6535 : return NULL;
488 : :
489 : : #if G_ENABLE_DEBUG
490 : 6012 : g_assert (data_elt >= data->data && data_elt < &data->data[data->len]);
491 : : #endif
492 : :
493 : 6012 : if (out_idx)
494 : 4538 : *out_idx = (data_elt - data->data);
495 : 6012 : return data_elt;
496 : : }
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 : 8209642 : g_datalist_clear (GData **datalist)
508 : : {
509 : : GData *data;
510 : :
511 : 8209642 : g_return_if_fail (datalist != NULL);
512 : :
513 : 8209642 : data = g_datalist_lock_and_get (datalist);
514 : :
515 : 8209642 : if (!data)
516 : : {
517 : 114 : g_datalist_unlock (datalist);
518 : 114 : return;
519 : : }
520 : :
521 : 8209528 : g_datalist_unlock_and_set (datalist, NULL);
522 : :
523 : 8209528 : datalist_destroy (data);
524 : : }
525 : :
526 : : /* HOLDS: g_dataset_global_lock */
527 : : static inline GDataset*
528 : 32 : g_dataset_lookup (gconstpointer dataset_location)
529 : : {
530 : : GDataset *dataset;
531 : :
532 : 32 : if (g_dataset_cached && g_dataset_cached->location == dataset_location)
533 : 14 : return g_dataset_cached;
534 : :
535 : 18 : dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
536 : 18 : if (dataset)
537 : 7 : g_dataset_cached = dataset;
538 : :
539 : 18 : return dataset;
540 : : }
541 : :
542 : : /* HOLDS: g_dataset_global_lock */
543 : : static void
544 : 7 : g_dataset_destroy_internal (GDataset *dataset)
545 : : {
546 : : gconstpointer dataset_location;
547 : :
548 : 7 : dataset_location = dataset->location;
549 : 9 : while (dataset)
550 : : {
551 : : GData *data;
552 : :
553 : 9 : data = G_DATALIST_GET_POINTER (&dataset->datalist);
554 : :
555 : 9 : if (!data)
556 : : {
557 : 7 : if (dataset == g_dataset_cached)
558 : 7 : g_dataset_cached = NULL;
559 : 7 : g_hash_table_remove (g_dataset_location_ht, dataset_location);
560 : 7 : g_slice_free (GDataset, dataset);
561 : 7 : break;
562 : : }
563 : :
564 : 2 : G_DATALIST_SET_POINTER (&dataset->datalist, NULL);
565 : :
566 : 2 : G_UNLOCK (g_dataset_global);
567 : :
568 : 2 : datalist_destroy (data);
569 : :
570 : 2 : G_LOCK (g_dataset_global);
571 : 2 : dataset = g_dataset_lookup (dataset_location);
572 : : }
573 : 7 : }
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 : 2 : g_dataset_destroy (gconstpointer dataset_location)
584 : : {
585 : 2 : g_return_if_fail (dataset_location != NULL);
586 : :
587 : 2 : G_LOCK (g_dataset_global);
588 : 2 : if (g_dataset_location_ht)
589 : : {
590 : : GDataset *dataset;
591 : :
592 : 2 : dataset = g_dataset_lookup (dataset_location);
593 : 2 : if (dataset)
594 : 2 : g_dataset_destroy_internal (dataset);
595 : : }
596 : 2 : G_UNLOCK (g_dataset_global);
597 : : }
598 : :
599 : : /* HOLDS: g_dataset_global_lock if dataset != null */
600 : : static inline gpointer
601 : 14159 : g_data_set_internal (GData **datalist,
602 : : GQuark key_id,
603 : : gpointer new_data,
604 : : GDestroyNotify new_destroy_func,
605 : : GDataset *dataset)
606 : : {
607 : : GData *d;
608 : 14159 : GData *new_d = NULL;
609 : : GDataElt old, *data;
610 : : guint32 idx;
611 : :
612 : : #ifdef G_ENABLE_DEBUG
613 : 14159 : g_assert (key_id != 0);
614 : : #endif
615 : :
616 : 14159 : d = g_datalist_lock_and_get (datalist);
617 : :
618 : 14159 : data = datalist_find (d, key_id, &idx);
619 : :
620 : 14159 : if (new_data == NULL) /* remove */
621 : : {
622 : 5051 : if (data)
623 : : {
624 : : GData *d_to_free;
625 : :
626 : 1229 : old = *data;
627 : :
628 : 1229 : datalist_remove (d, idx);
629 : 1229 : if (datalist_shrink (&d, &d_to_free))
630 : : {
631 : 15 : 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 : 15 : if (dataset && !d)
637 : 5 : g_dataset_destroy_internal (dataset);
638 : :
639 : 15 : if (G_UNLIKELY (d_to_free))
640 : 15 : g_free (d_to_free);
641 : : }
642 : : else
643 : 1214 : 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 : 1229 : if (old.destroy && !new_destroy_func)
653 : : {
654 : 7 : if (dataset)
655 : 2 : G_UNLOCK (g_dataset_global);
656 : 7 : old.destroy (old.data);
657 : 7 : if (dataset)
658 : 2 : G_LOCK (g_dataset_global);
659 : 7 : old.data = NULL;
660 : : }
661 : :
662 : 1229 : return old.data;
663 : : }
664 : : }
665 : : else
666 : : {
667 : 9108 : if (data)
668 : : {
669 : 1411 : if (!data->destroy)
670 : : {
671 : 1297 : data->data = new_data;
672 : 1297 : data->destroy = new_destroy_func;
673 : 1297 : g_datalist_unlock (datalist);
674 : : }
675 : : else
676 : : {
677 : 114 : old = *data;
678 : 114 : data->data = new_data;
679 : 114 : data->destroy = new_destroy_func;
680 : :
681 : 114 : 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 : 114 : if (dataset)
688 : 0 : G_UNLOCK (g_dataset_global);
689 : 114 : old.destroy (old.data);
690 : 114 : if (dataset)
691 : 0 : G_LOCK (g_dataset_global);
692 : : }
693 : 1411 : return NULL;
694 : : }
695 : :
696 : : /* The key was not found, insert it */
697 : 7697 : if (datalist_append (&d, key_id, new_data, new_destroy_func))
698 : 3218 : new_d = d;
699 : : }
700 : :
701 : 11519 : if (new_d)
702 : 3218 : g_datalist_unlock_and_set (datalist, new_d);
703 : : else
704 : 8301 : g_datalist_unlock (datalist);
705 : :
706 : 11519 : return NULL;
707 : :
708 : : }
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 : 8 : g_datalist_id_remove_multiple (GData **datalist,
728 : : GQuark *keys,
729 : : gsize n_keys)
730 : : {
731 : : GData *d;
732 : : GDataElt *old;
733 : 8 : GDataElt *old_to_free = NULL;
734 : : GData *d_to_free;
735 : : gsize found_keys;
736 : : gsize i_keys;
737 : :
738 : 8 : if (n_keys == 0)
739 : 0 : return;
740 : :
741 : 8 : d = g_datalist_lock_and_get (datalist);
742 : :
743 : 8 : if (!d)
744 : : {
745 : 0 : g_datalist_unlock (datalist);
746 : 0 : return;
747 : : }
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 : 8 : if (n_keys <= 400u / sizeof (GDataElt))
756 : 2 : old = g_newa (GDataElt, n_keys);
757 : : else
758 : : {
759 : 6 : old_to_free = g_new (GDataElt, n_keys);
760 : 6 : old = old_to_free;
761 : : }
762 : :
763 : 8 : found_keys = 0;
764 : 3576 : for (i_keys = 0; i_keys < n_keys; i_keys++)
765 : : {
766 : : GDataElt *data_elt;
767 : : guint32 idx;
768 : :
769 : 3568 : data_elt = datalist_find (d, keys[i_keys], &idx);
770 : 3568 : if (!data_elt)
771 : 1502 : 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 : 2066 : old[found_keys++] = *data_elt;
779 : 2066 : datalist_remove (d, idx);
780 : : }
781 : :
782 : 8 : if (found_keys > 0 && datalist_shrink (&d, &d_to_free))
783 : : {
784 : 3 : g_datalist_unlock_and_set (datalist, d);
785 : 3 : if (G_UNLIKELY (d_to_free))
786 : 3 : g_free (d_to_free);
787 : : }
788 : : else
789 : 5 : g_datalist_unlock (datalist);
790 : :
791 : 2074 : for (i_keys = 0; i_keys < found_keys; i_keys++)
792 : : {
793 : 2066 : if (old[i_keys].destroy)
794 : 3 : old[i_keys].destroy (old[i_keys].data);
795 : : }
796 : :
797 : 8 : if (G_UNLIKELY (old_to_free))
798 : 6 : g_free (old_to_free);
799 : : }
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 : 17 : g_dataset_id_set_data_full (gconstpointer dataset_location,
864 : : GQuark key_id,
865 : : gpointer data,
866 : : GDestroyNotify destroy_func)
867 : : {
868 : : GDataset *dataset;
869 : :
870 : 17 : g_return_if_fail (dataset_location != NULL);
871 : 17 : if (!data)
872 : 4 : g_return_if_fail (destroy_func == NULL);
873 : 17 : if (!key_id)
874 : : {
875 : 0 : if (data)
876 : 0 : g_return_if_fail (key_id > 0);
877 : : else
878 : 0 : return;
879 : : }
880 : :
881 : 17 : G_LOCK (g_dataset_global);
882 : 17 : if (!g_dataset_location_ht)
883 : 1 : g_data_initialize ();
884 : :
885 : 17 : dataset = g_dataset_lookup (dataset_location);
886 : 17 : if (!dataset)
887 : : {
888 : 7 : dataset = g_slice_new (GDataset);
889 : 7 : dataset->location = dataset_location;
890 : 7 : g_datalist_init (&dataset->datalist);
891 : 7 : g_hash_table_insert (g_dataset_location_ht,
892 : 7 : (gpointer) dataset->location,
893 : : dataset);
894 : : }
895 : :
896 : 17 : g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
897 : 17 : G_UNLOCK (g_dataset_global);
898 : : }
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 : 14205 : g_datalist_id_set_data_full (GData **datalist,
968 : : GQuark key_id,
969 : : gpointer data,
970 : : GDestroyNotify destroy_func)
971 : : {
972 : 14205 : g_return_if_fail (datalist != NULL);
973 : 14205 : if (!data)
974 : 5110 : g_return_if_fail (destroy_func == NULL);
975 : 14205 : if (!key_id)
976 : : {
977 : 67 : if (data)
978 : 0 : g_return_if_fail (key_id > 0);
979 : : else
980 : 67 : return;
981 : : }
982 : :
983 : 14138 : g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
984 : : }
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 : 1 : g_dataset_id_remove_no_notify (gconstpointer dataset_location,
1006 : : GQuark key_id)
1007 : : {
1008 : 1 : gpointer ret_data = NULL;
1009 : :
1010 : 1 : g_return_val_if_fail (dataset_location != NULL, NULL);
1011 : :
1012 : 1 : if (key_id == 0)
1013 : 0 : return NULL;
1014 : :
1015 : 1 : G_LOCK (g_dataset_global);
1016 : 1 : if (g_dataset_location_ht)
1017 : : {
1018 : : GDataset *dataset;
1019 : :
1020 : 1 : dataset = g_dataset_lookup (dataset_location);
1021 : 1 : if (dataset)
1022 : 1 : ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
1023 : : }
1024 : 1 : G_UNLOCK (g_dataset_global);
1025 : :
1026 : 1 : return ret_data;
1027 : : }
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 : 3 : g_datalist_id_remove_no_notify (GData **datalist,
1049 : : GQuark key_id)
1050 : : {
1051 : 3 : gpointer ret_data = NULL;
1052 : :
1053 : 3 : g_return_val_if_fail (datalist != NULL, NULL);
1054 : :
1055 : 3 : if (key_id)
1056 : 3 : ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
1057 : :
1058 : 3 : return ret_data;
1059 : : }
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 : 64337443 : g_datalist_id_update_atomic (GData **datalist,
1101 : : GQuark key_id,
1102 : : gboolean already_locked,
1103 : : GDataListUpdateAtomicFunc callback,
1104 : : gpointer user_data)
1105 : : {
1106 : : GData *d;
1107 : : GDataElt *data;
1108 : : gpointer new_data;
1109 : : gpointer result;
1110 : : GDestroyNotify new_destroy;
1111 : : guint32 idx;
1112 : :
1113 : 64337443 : g_return_val_if_fail (datalist, NULL);
1114 : 64337443 : g_return_val_if_fail (key_id != 0, NULL);
1115 : :
1116 : 64337443 : if (G_UNLIKELY (already_locked))
1117 : : {
1118 : 1858408 : d = G_DATALIST_GET_POINTER (datalist);
1119 : : }
1120 : : else
1121 : : {
1122 : 62479035 : d = g_datalist_lock_and_get (datalist);
1123 : : }
1124 : :
1125 : 64337443 : data = datalist_find (d, key_id, &idx);
1126 : :
1127 : 64337443 : if (data)
1128 : : {
1129 : 20022342 : new_data = data->data;
1130 : 20022342 : new_destroy = data->destroy;
1131 : : }
1132 : : else
1133 : : {
1134 : 44315101 : new_data = NULL;
1135 : 44315101 : new_destroy = NULL;
1136 : : }
1137 : :
1138 : 64337443 : result = callback (&new_data, &new_destroy, user_data);
1139 : :
1140 : 64337443 : if (G_LIKELY (data))
1141 : : {
1142 : 20022342 : if (G_LIKELY (data->data == new_data && data->destroy == new_destroy))
1143 : : {
1144 : : /* No change. */
1145 : : }
1146 : 4746461 : else if (!new_data)
1147 : : {
1148 : : 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 : 2423596 : datalist_remove (d, idx);
1154 : 2423596 : if (datalist_shrink (&d, &d_to_free))
1155 : : {
1156 : 2422738 : g_datalist_unlock_and_set (datalist, d);
1157 : 2422738 : if (G_UNLIKELY (d_to_free))
1158 : 2422738 : g_free (d_to_free);
1159 : 2422738 : goto return_without_unlock;
1160 : : }
1161 : : }
1162 : : else
1163 : : {
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 : 2322865 : data->data = new_data;
1170 : 2322865 : data->destroy = new_destroy;
1171 : : }
1172 : : }
1173 : : else
1174 : : {
1175 : 44315101 : if (G_LIKELY (!new_data))
1176 : : {
1177 : : /* No change. The entry didn't exist and still does not. */
1178 : : }
1179 : : else
1180 : : {
1181 : : /* Add. Add a new entry that didn't exist previously. */
1182 : 10641961 : if (datalist_append (&d, key_id, new_data, new_destroy))
1183 : : {
1184 : 10630680 : g_datalist_unlock_and_set (datalist, d);
1185 : 10630680 : goto return_without_unlock;
1186 : : }
1187 : : }
1188 : : }
1189 : :
1190 : 51284025 : g_datalist_unlock (datalist);
1191 : :
1192 : 64337443 : return_without_unlock:
1193 : 64337443 : return result;
1194 : : }
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 : 12 : g_dataset_id_get_data (gconstpointer dataset_location,
1218 : : GQuark key_id)
1219 : : {
1220 : 12 : gpointer retval = NULL;
1221 : :
1222 : 12 : g_return_val_if_fail (dataset_location != NULL, NULL);
1223 : :
1224 : 12 : if (key_id == 0)
1225 : 3 : return NULL;
1226 : :
1227 : 9 : G_LOCK (g_dataset_global);
1228 : 9 : if (g_dataset_location_ht)
1229 : : {
1230 : : GDataset *dataset;
1231 : :
1232 : 9 : dataset = g_dataset_lookup (dataset_location);
1233 : 9 : if (dataset)
1234 : 5 : retval = g_datalist_id_get_data (&dataset->datalist, key_id);
1235 : : }
1236 : 9 : G_UNLOCK (g_dataset_global);
1237 : :
1238 : 9 : return retval;
1239 : : }
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 : 74378 : g_datalist_id_get_data (GData **datalist,
1253 : : GQuark key_id)
1254 : : {
1255 : 74378 : return g_datalist_id_dup_data (datalist, key_id, NULL, NULL);
1256 : : }
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 : 74381 : g_datalist_id_dup_data (GData **datalist,
1302 : : GQuark key_id,
1303 : : GDuplicateFunc dup_func,
1304 : : gpointer user_data)
1305 : : {
1306 : 74381 : gpointer val = NULL;
1307 : 74381 : gpointer retval = NULL;
1308 : : GData *d;
1309 : : GDataElt *data;
1310 : :
1311 : 74381 : d = g_datalist_lock_and_get (datalist);
1312 : :
1313 : 74381 : data = datalist_find (d, key_id, NULL);
1314 : 74381 : if (data)
1315 : 73548 : val = data->data;
1316 : :
1317 : 74381 : if (dup_func)
1318 : 3 : retval = dup_func (val, user_data);
1319 : : else
1320 : 74378 : retval = val;
1321 : :
1322 : 74381 : g_datalist_unlock (datalist);
1323 : :
1324 : 74381 : return retval;
1325 : : }
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 : 229345 : 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 : : {
1363 : 229345 : gpointer val = NULL;
1364 : : GData *d;
1365 : : GDataElt *data;
1366 : 229345 : GData *d_to_free = NULL;
1367 : 229345 : gboolean set_d = FALSE;
1368 : : guint32 idx;
1369 : :
1370 : 229345 : g_return_val_if_fail (datalist != NULL, FALSE);
1371 : 229345 : g_return_val_if_fail (key_id != 0, FALSE);
1372 : :
1373 : 229345 : if (old_destroy)
1374 : 5 : *old_destroy = NULL;
1375 : :
1376 : 229345 : d = g_datalist_lock_and_get (datalist);
1377 : :
1378 : 229345 : data = datalist_find (d, key_id, &idx);
1379 : 229345 : if (data)
1380 : : {
1381 : 229343 : val = data->data;
1382 : 229343 : if (val == oldval)
1383 : : {
1384 : 100002 : if (old_destroy)
1385 : 2 : *old_destroy = data->destroy;
1386 : 100002 : if (newval != NULL)
1387 : : {
1388 : 100001 : data->data = newval;
1389 : 100001 : data->destroy = destroy;
1390 : : }
1391 : : else
1392 : : {
1393 : 1 : datalist_remove (d, idx);
1394 : 1 : if (datalist_shrink (&d, &d_to_free))
1395 : 1 : set_d = TRUE;
1396 : : }
1397 : : }
1398 : : }
1399 : : else
1400 : : {
1401 : 2 : if (oldval == NULL && newval != NULL)
1402 : : {
1403 : 2 : if (datalist_append (&d, key_id, newval, destroy))
1404 : 2 : set_d = TRUE;
1405 : : }
1406 : : }
1407 : :
1408 : 229345 : if (set_d)
1409 : 3 : g_datalist_unlock_and_set (datalist, d);
1410 : : else
1411 : 229342 : g_datalist_unlock (datalist);
1412 : :
1413 : 229345 : if (G_UNLIKELY (d_to_free))
1414 : 1 : g_free (d_to_free);
1415 : :
1416 : 229345 : return val == oldval;
1417 : : }
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 : 288422 : g_datalist_get_data (GData **datalist,
1432 : : const gchar *key)
1433 : : {
1434 : : GQuark key_id;
1435 : : GHashTable *index;
1436 : 288422 : gpointer res = NULL;
1437 : : GDataElt *data_elt;
1438 : : GData *d;
1439 : :
1440 : 288422 : g_return_val_if_fail (datalist != NULL, NULL);
1441 : :
1442 : 288422 : if (G_UNLIKELY (!key))
1443 : 4 : return NULL;
1444 : :
1445 : 288418 : d = g_datalist_lock_and_get (datalist);
1446 : :
1447 : 288418 : if (!d)
1448 : 360 : goto out;
1449 : :
1450 : 288058 : index = datalist_index_get (d);
1451 : :
1452 : 288058 : if (G_LIKELY (!index))
1453 : : {
1454 : : guint32 i;
1455 : :
1456 : 289591 : for (i = 0; i < d->len; i++)
1457 : : {
1458 : : const char *qstr;
1459 : :
1460 : 288588 : 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 : 288588 : qstr = g_quark_to_string (data_elt->key);
1468 : 288588 : if (qstr && strcmp (qstr, key) == 0)
1469 : : {
1470 : 285580 : res = data_elt->data;
1471 : 285580 : goto out;
1472 : : }
1473 : : }
1474 : 1003 : goto out;
1475 : : }
1476 : :
1477 : 1475 : key_id = g_quark_try_string (key);
1478 : 1475 : if (key_id == 0)
1479 : 0 : goto out;
1480 : :
1481 : 1475 : data_elt = g_hash_table_lookup (index, &key_id);
1482 : :
1483 : 1475 : if (data_elt)
1484 : 1474 : res = data_elt->data;
1485 : :
1486 : 1 : out:
1487 : 288418 : g_datalist_unlock (datalist);
1488 : :
1489 : 288418 : return res;
1490 : : }
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 : 1 : g_dataset_foreach (gconstpointer dataset_location,
1520 : : GDataForeachFunc func,
1521 : : gpointer user_data)
1522 : : {
1523 : : GDataset *dataset;
1524 : :
1525 : 1 : g_return_if_fail (dataset_location != NULL);
1526 : 1 : g_return_if_fail (func != NULL);
1527 : :
1528 : 1 : G_LOCK (g_dataset_global);
1529 : 1 : if (g_dataset_location_ht)
1530 : : {
1531 : 1 : dataset = g_dataset_lookup (dataset_location);
1532 : 1 : G_UNLOCK (g_dataset_global);
1533 : 1 : if (dataset)
1534 : 1 : g_datalist_foreach (&dataset->datalist, func, user_data);
1535 : : }
1536 : : else
1537 : : {
1538 : 0 : G_UNLOCK (g_dataset_global);
1539 : : }
1540 : : }
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 : 3 : g_datalist_foreach (GData **datalist,
1561 : : GDataForeachFunc func,
1562 : : gpointer user_data)
1563 : : {
1564 : : GData *d;
1565 : : guint i, j, len;
1566 : : GQuark *keys;
1567 : :
1568 : 3 : g_return_if_fail (datalist != NULL);
1569 : 3 : g_return_if_fail (func != NULL);
1570 : :
1571 : 3 : d = G_DATALIST_GET_POINTER (datalist);
1572 : 3 : if (d == NULL)
1573 : 1 : return;
1574 : :
1575 : : /* We make a copy of the keys so that we can handle it changing
1576 : : in the callback */
1577 : 2 : len = d->len;
1578 : 2 : keys = g_new (GQuark, len);
1579 : 8 : for (i = 0; i < len; i++)
1580 : 6 : keys[i] = d->data[i].key;
1581 : :
1582 : 8 : for (i = 0; i < len; i++)
1583 : : {
1584 : : /* A previous callback might have removed a later item, so always check that
1585 : : it still exists before calling */
1586 : 6 : d = G_DATALIST_GET_POINTER (datalist);
1587 : :
1588 : 6 : if (d == NULL)
1589 : 0 : break;
1590 : 12 : for (j = 0; j < d->len; j++)
1591 : : {
1592 : 12 : if (d->data[j].key == keys[i]) {
1593 : 6 : func (d->data[i].key, d->data[i].data, user_data);
1594 : 6 : break;
1595 : : }
1596 : : }
1597 : : }
1598 : 2 : g_free (keys);
1599 : : }
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 : 29 : g_datalist_init (GData **datalist)
1610 : : {
1611 : 29 : g_return_if_fail (datalist != NULL);
1612 : :
1613 : 29 : g_atomic_pointer_set (datalist, NULL);
1614 : : }
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 : 111424 : g_datalist_set_flags (GData **datalist,
1636 : : guint flags)
1637 : : {
1638 : 111424 : g_return_if_fail (datalist != NULL);
1639 : 111424 : g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1640 : :
1641 : 111424 : g_atomic_pointer_or (datalist, (gsize)flags);
1642 : : }
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 : 100015 : g_datalist_unset_flags (GData **datalist,
1659 : : guint flags)
1660 : : {
1661 : 100015 : g_return_if_fail (datalist != NULL);
1662 : 100015 : g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
1663 : :
1664 : 100015 : g_atomic_pointer_and (datalist, ~(gsize)flags);
1665 : : }
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 : 229454738 : g_datalist_get_flags (GData **datalist)
1680 : : {
1681 : 229454738 : g_return_val_if_fail (datalist != NULL, 0);
1682 : :
1683 : 229454738 : return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
1684 : : }
1685 : :
1686 : : /* HOLDS: g_dataset_global_lock */
1687 : : static void
1688 : 1 : g_data_initialize (void)
1689 : : {
1690 : 1 : g_return_if_fail (g_dataset_location_ht == NULL);
1691 : :
1692 : 1 : g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
1693 : 1 : g_dataset_cached = NULL;
1694 : : }
|