/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 | } |