/src/pango/subprojects/glib/gobject/gvalue.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GObject - GLib Type, Object, Parameter and Signal Library |
2 | | * Copyright (C) 1997-1999, 2000-2001 Tim Janik and Red Hat, Inc. |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General |
17 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | /* |
21 | | * FIXME: MT-safety |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include <string.h> |
27 | | |
28 | | #include "gvalue.h" |
29 | | #include "gvaluecollector.h" |
30 | | #include "gbsearcharray.h" |
31 | | #include "gtype-private.h" |
32 | | |
33 | | |
34 | | /* --- typedefs & structures --- */ |
35 | | typedef struct { |
36 | | GType src_type; |
37 | | GType dest_type; |
38 | | GValueTransform func; |
39 | | } TransformEntry; |
40 | | |
41 | | |
42 | | /* --- prototypes --- */ |
43 | | static gint transform_entries_cmp (gconstpointer bsearch_node1, |
44 | | gconstpointer bsearch_node2); |
45 | | |
46 | | |
47 | | /* --- variables --- */ |
48 | | static GBSearchArray *transform_array = NULL; |
49 | | static GBSearchConfig transform_bconfig = { |
50 | | sizeof (TransformEntry), |
51 | | transform_entries_cmp, |
52 | | G_BSEARCH_ARRAY_ALIGN_POWER2, |
53 | | }; |
54 | | |
55 | | |
56 | | /* --- functions --- */ |
57 | | void |
58 | | _g_value_c_init (void) |
59 | 12 | { |
60 | 12 | transform_array = g_bsearch_array_create (&transform_bconfig); |
61 | 12 | } |
62 | | |
63 | | static inline void /* keep this function in sync with gvaluecollector.h and gboxed.c */ |
64 | | value_meminit (GValue *value, |
65 | | GType value_type) |
66 | 0 | { |
67 | 0 | value->g_type = value_type; |
68 | 0 | memset (value->data, 0, sizeof (value->data)); |
69 | 0 | } |
70 | | |
71 | | /** |
72 | | * g_value_init: |
73 | | * @value: A zero-filled (uninitialized) #GValue structure. |
74 | | * @g_type: Type the #GValue should hold values of. |
75 | | * |
76 | | * Initializes @value with the default value of @type. |
77 | | * |
78 | | * Returns: (transfer none): the #GValue structure that has been passed in |
79 | | */ |
80 | | GValue* |
81 | | g_value_init (GValue *value, |
82 | | GType g_type) |
83 | 0 | { |
84 | 0 | GTypeValueTable *value_table; |
85 | | /* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL); be more elaborate below */ |
86 | 0 | g_return_val_if_fail (value != NULL, NULL); |
87 | | /* g_return_val_if_fail (G_VALUE_TYPE (value) == 0, NULL); be more elaborate below */ |
88 | | |
89 | 0 | value_table = g_type_value_table_peek (g_type); |
90 | |
|
91 | 0 | if (value_table && G_VALUE_TYPE (value) == 0) |
92 | 0 | { |
93 | | /* setup and init */ |
94 | 0 | value_meminit (value, g_type); |
95 | 0 | value_table->value_init (value); |
96 | 0 | } |
97 | 0 | else if (G_VALUE_TYPE (value)) |
98 | 0 | g_critical ("%s: cannot initialize GValue with type '%s', the value has already been initialized as '%s'", |
99 | 0 | G_STRLOC, |
100 | 0 | g_type_name (g_type), |
101 | 0 | g_type_name (G_VALUE_TYPE (value))); |
102 | 0 | else /* !G_TYPE_IS_VALUE (g_type) */ |
103 | 0 | g_critical ("%s: cannot initialize GValue with type '%s', %s", |
104 | 0 | G_STRLOC, |
105 | 0 | g_type_name (g_type), |
106 | 0 | value_table ? "this type is abstract with regards to GValue use, use a more specific (derived) type" : "this type has no GTypeValueTable implementation"); |
107 | 0 | return value; |
108 | 0 | } |
109 | | |
110 | | /** |
111 | | * g_value_copy: |
112 | | * @src_value: An initialized #GValue structure. |
113 | | * @dest_value: An initialized #GValue structure of the same type as @src_value. |
114 | | * |
115 | | * Copies the value of @src_value into @dest_value. |
116 | | */ |
117 | | void |
118 | | g_value_copy (const GValue *src_value, |
119 | | GValue *dest_value) |
120 | 0 | { |
121 | 0 | g_return_if_fail (src_value); |
122 | 0 | g_return_if_fail (dest_value); |
123 | 0 | g_return_if_fail (g_value_type_compatible (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value))); |
124 | | |
125 | 0 | if (src_value != dest_value) |
126 | 0 | { |
127 | 0 | GType dest_type = G_VALUE_TYPE (dest_value); |
128 | 0 | GTypeValueTable *value_table = g_type_value_table_peek (dest_type); |
129 | |
|
130 | 0 | g_return_if_fail (value_table); |
131 | | |
132 | | /* make sure dest_value's value is free()d */ |
133 | 0 | if (value_table->value_free) |
134 | 0 | value_table->value_free (dest_value); |
135 | | |
136 | | /* setup and copy */ |
137 | 0 | value_meminit (dest_value, dest_type); |
138 | 0 | value_table->value_copy (src_value, dest_value); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | /** |
143 | | * g_value_reset: |
144 | | * @value: An initialized #GValue structure. |
145 | | * |
146 | | * Clears the current value in @value and resets it to the default value |
147 | | * (as if the value had just been initialized). |
148 | | * |
149 | | * Returns: the #GValue structure that has been passed in |
150 | | */ |
151 | | GValue* |
152 | | g_value_reset (GValue *value) |
153 | 0 | { |
154 | 0 | GTypeValueTable *value_table; |
155 | 0 | GType g_type; |
156 | |
|
157 | 0 | g_return_val_if_fail (value, NULL); |
158 | 0 | g_type = G_VALUE_TYPE (value); |
159 | |
|
160 | 0 | value_table = g_type_value_table_peek (g_type); |
161 | 0 | g_return_val_if_fail (value_table, NULL); |
162 | | |
163 | | /* make sure value's value is free()d */ |
164 | 0 | if (value_table->value_free) |
165 | 0 | value_table->value_free (value); |
166 | | |
167 | | /* setup and init */ |
168 | 0 | value_meminit (value, g_type); |
169 | 0 | value_table->value_init (value); |
170 | |
|
171 | 0 | return value; |
172 | 0 | } |
173 | | |
174 | | /** |
175 | | * g_value_unset: |
176 | | * @value: An initialized #GValue structure. |
177 | | * |
178 | | * Clears the current value in @value (if any) and "unsets" the type, |
179 | | * this releases all resources associated with this GValue. An unset |
180 | | * value is the same as an uninitialized (zero-filled) #GValue |
181 | | * structure. |
182 | | */ |
183 | | void |
184 | | g_value_unset (GValue *value) |
185 | 0 | { |
186 | 0 | GTypeValueTable *value_table; |
187 | | |
188 | 0 | if (value->g_type == 0) |
189 | 0 | return; |
190 | | |
191 | 0 | g_return_if_fail (value); |
192 | | |
193 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
194 | 0 | g_return_if_fail (value_table); |
195 | | |
196 | 0 | if (value_table->value_free) |
197 | 0 | value_table->value_free (value); |
198 | 0 | memset (value, 0, sizeof (*value)); |
199 | 0 | } |
200 | | |
201 | | /** |
202 | | * g_value_fits_pointer: |
203 | | * @value: An initialized #GValue structure. |
204 | | * |
205 | | * Determines if @value will fit inside the size of a pointer value. |
206 | | * This is an internal function introduced mainly for C marshallers. |
207 | | * |
208 | | * Returns: %TRUE if @value will fit inside a pointer value. |
209 | | */ |
210 | | gboolean |
211 | | g_value_fits_pointer (const GValue *value) |
212 | 0 | { |
213 | 0 | GTypeValueTable *value_table; |
214 | |
|
215 | 0 | g_return_val_if_fail (value, FALSE); |
216 | | |
217 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
218 | 0 | g_return_val_if_fail (value_table, FALSE); |
219 | | |
220 | 0 | return value_table->value_peek_pointer != NULL; |
221 | 0 | } |
222 | | |
223 | | /** |
224 | | * g_value_peek_pointer: |
225 | | * @value: An initialized #GValue structure |
226 | | * |
227 | | * Returns the value contents as pointer. This function asserts that |
228 | | * g_value_fits_pointer() returned %TRUE for the passed in value. |
229 | | * This is an internal function introduced mainly for C marshallers. |
230 | | * |
231 | | * Returns: (transfer none): the value contents as pointer |
232 | | */ |
233 | | gpointer |
234 | | g_value_peek_pointer (const GValue *value) |
235 | 0 | { |
236 | 0 | GTypeValueTable *value_table; |
237 | |
|
238 | 0 | g_return_val_if_fail (value, NULL); |
239 | | |
240 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
241 | 0 | g_return_val_if_fail (value_table, NULL); |
242 | | |
243 | 0 | if (!value_table->value_peek_pointer) |
244 | 0 | { |
245 | 0 | g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL); |
246 | 0 | return NULL; |
247 | 0 | } |
248 | | |
249 | 0 | return value_table->value_peek_pointer (value); |
250 | 0 | } |
251 | | |
252 | | /** |
253 | | * g_value_set_instance: |
254 | | * @value: An initialized #GValue structure. |
255 | | * @instance: (nullable): the instance |
256 | | * |
257 | | * Sets @value from an instantiatable type via the |
258 | | * value_table's collect_value() function. |
259 | | */ |
260 | | void |
261 | | g_value_set_instance (GValue *value, |
262 | | gpointer instance) |
263 | 0 | { |
264 | 0 | GType g_type; |
265 | 0 | GTypeValueTable *value_table; |
266 | 0 | GTypeCValue cvalue; |
267 | 0 | gchar *error_msg; |
268 | |
|
269 | 0 | g_return_if_fail (value); |
270 | 0 | g_type = G_VALUE_TYPE (value); |
271 | 0 | value_table = g_type_value_table_peek (g_type); |
272 | 0 | g_return_if_fail (value_table); |
273 | | |
274 | 0 | if (instance) |
275 | 0 | { |
276 | 0 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
277 | 0 | g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value))); |
278 | 0 | } |
279 | | |
280 | 0 | g_return_if_fail (strcmp (value_table->collect_format, "p") == 0); |
281 | | |
282 | 0 | memset (&cvalue, 0, sizeof (cvalue)); |
283 | 0 | cvalue.v_pointer = instance; |
284 | | |
285 | | /* make sure value's value is free()d */ |
286 | 0 | if (value_table->value_free) |
287 | 0 | value_table->value_free (value); |
288 | | |
289 | | /* setup and collect */ |
290 | 0 | value_meminit (value, g_type); |
291 | 0 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
292 | 0 | if (error_msg) |
293 | 0 | { |
294 | 0 | g_critical ("%s: %s", G_STRLOC, error_msg); |
295 | 0 | g_free (error_msg); |
296 | | |
297 | | /* we purposely leak the value here, it might not be |
298 | | * in a correct state if an error condition occurred |
299 | | */ |
300 | 0 | value_meminit (value, g_type); |
301 | 0 | value_table->value_init (value); |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | /** |
306 | | * g_value_init_from_instance: |
307 | | * @value: An uninitialized #GValue structure. |
308 | | * @instance: (type GObject.TypeInstance): the instance |
309 | | * |
310 | | * Initializes and sets @value from an instantiatable type via the |
311 | | * value_table's collect_value() function. |
312 | | * |
313 | | * Note: The @value will be initialised with the exact type of |
314 | | * @instance. If you wish to set the @value's type to a different GType |
315 | | * (such as a parent class GType), you need to manually call |
316 | | * g_value_init() and g_value_set_instance(). |
317 | | * |
318 | | * Since: 2.42 |
319 | | */ |
320 | | void |
321 | | g_value_init_from_instance (GValue *value, |
322 | | gpointer instance) |
323 | 0 | { |
324 | 0 | g_return_if_fail (value != NULL && G_VALUE_TYPE(value) == 0); |
325 | | |
326 | 0 | if (G_IS_OBJECT (instance)) |
327 | 0 | { |
328 | | /* Fast-path. |
329 | | * If G_IS_OBJECT() succeeds we know: |
330 | | * * that instance is present and valid |
331 | | * * that it is a GObject, and therefore we can directly |
332 | | * use the collect implementation (g_object_ref) */ |
333 | 0 | value_meminit (value, G_TYPE_FROM_INSTANCE (instance)); |
334 | 0 | value->data[0].v_pointer = g_object_ref (instance); |
335 | 0 | } |
336 | 0 | else |
337 | 0 | { |
338 | 0 | GType g_type; |
339 | 0 | GTypeValueTable *value_table; |
340 | 0 | GTypeCValue cvalue; |
341 | 0 | gchar *error_msg; |
342 | |
|
343 | 0 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
344 | | |
345 | 0 | g_type = G_TYPE_FROM_INSTANCE (instance); |
346 | 0 | value_table = g_type_value_table_peek (g_type); |
347 | 0 | g_return_if_fail (strcmp (value_table->collect_format, "p") == 0); |
348 | | |
349 | 0 | memset (&cvalue, 0, sizeof (cvalue)); |
350 | 0 | cvalue.v_pointer = instance; |
351 | | |
352 | | /* setup and collect */ |
353 | 0 | value_meminit (value, g_type); |
354 | 0 | value_table->value_init (value); |
355 | 0 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
356 | 0 | if (error_msg) |
357 | 0 | { |
358 | 0 | g_critical ("%s: %s", G_STRLOC, error_msg); |
359 | 0 | g_free (error_msg); |
360 | | |
361 | | /* we purposely leak the value here, it might not be |
362 | | * in a correct state if an error condition occurred |
363 | | */ |
364 | 0 | value_meminit (value, g_type); |
365 | 0 | value_table->value_init (value); |
366 | 0 | } |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | static GType |
371 | | transform_lookup_get_parent_type (GType type) |
372 | 0 | { |
373 | 0 | if (g_type_fundamental (type) == G_TYPE_INTERFACE) |
374 | 0 | return g_type_interface_instantiatable_prerequisite (type); |
375 | | |
376 | 0 | return g_type_parent (type); |
377 | 0 | } |
378 | | |
379 | | static GValueTransform |
380 | | transform_func_lookup (GType src_type, |
381 | | GType dest_type) |
382 | 0 | { |
383 | 0 | TransformEntry entry; |
384 | |
|
385 | 0 | entry.src_type = src_type; |
386 | 0 | do |
387 | 0 | { |
388 | 0 | entry.dest_type = dest_type; |
389 | 0 | do |
390 | 0 | { |
391 | 0 | TransformEntry *e; |
392 | | |
393 | 0 | e = g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry); |
394 | 0 | if (e) |
395 | 0 | { |
396 | | /* need to check that there hasn't been a change in value handling */ |
397 | 0 | if (g_type_value_table_peek (entry.dest_type) == g_type_value_table_peek (dest_type) && |
398 | 0 | g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type)) |
399 | 0 | return e->func; |
400 | 0 | } |
401 | 0 | entry.dest_type = transform_lookup_get_parent_type (entry.dest_type); |
402 | 0 | } |
403 | 0 | while (entry.dest_type); |
404 | | |
405 | 0 | entry.src_type = transform_lookup_get_parent_type (entry.src_type); |
406 | 0 | } |
407 | 0 | while (entry.src_type); |
408 | | |
409 | 0 | return NULL; |
410 | 0 | } |
411 | | |
412 | | static gint |
413 | | transform_entries_cmp (gconstpointer bsearch_node1, |
414 | | gconstpointer bsearch_node2) |
415 | 24.1k | { |
416 | 24.1k | const TransformEntry *e1 = bsearch_node1; |
417 | 24.1k | const TransformEntry *e2 = bsearch_node2; |
418 | 24.1k | gint cmp = G_BSEARCH_ARRAY_CMP (e1->src_type, e2->src_type); |
419 | | |
420 | 24.1k | if (cmp) |
421 | 16.2k | return cmp; |
422 | 7.92k | else |
423 | 7.92k | return G_BSEARCH_ARRAY_CMP (e1->dest_type, e2->dest_type); |
424 | 24.1k | } |
425 | | |
426 | | /** |
427 | | * g_value_register_transform_func: (skip) |
428 | | * @src_type: Source type. |
429 | | * @dest_type: Target type. |
430 | | * @transform_func: a function which transforms values of type @src_type |
431 | | * into value of type @dest_type |
432 | | * |
433 | | * Registers a value transformation function for use in g_value_transform(). |
434 | | * A previously registered transformation function for @src_type and @dest_type |
435 | | * will be replaced. |
436 | | */ |
437 | | void |
438 | | g_value_register_transform_func (GType src_type, |
439 | | GType dest_type, |
440 | | GValueTransform transform_func) |
441 | 2.04k | { |
442 | 2.04k | TransformEntry entry; |
443 | | |
444 | | /* these checks won't pass for dynamic types. |
445 | | * g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (src_type)); |
446 | | * g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (dest_type)); |
447 | | */ |
448 | 2.04k | g_return_if_fail (transform_func != NULL); |
449 | | |
450 | 2.04k | entry.src_type = src_type; |
451 | 2.04k | entry.dest_type = dest_type; |
452 | | |
453 | | #if 0 /* let transform function replacement be a valid operation */ |
454 | | if (g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry)) |
455 | | g_warning ("reregistering value transformation function (%p) for '%s' to '%s'", |
456 | | transform_func, |
457 | | g_type_name (src_type), |
458 | | g_type_name (dest_type)); |
459 | | #endif |
460 | | |
461 | 2.04k | entry.func = transform_func; |
462 | 2.04k | transform_array = g_bsearch_array_replace (transform_array, &transform_bconfig, &entry); |
463 | 2.04k | } |
464 | | |
465 | | /** |
466 | | * g_value_type_transformable: |
467 | | * @src_type: Source type. |
468 | | * @dest_type: Target type. |
469 | | * |
470 | | * Check whether g_value_transform() is able to transform values |
471 | | * of type @src_type into values of type @dest_type. Note that for |
472 | | * the types to be transformable, they must be compatible or a |
473 | | * transformation function must be registered. |
474 | | * |
475 | | * Returns: %TRUE if the transformation is possible, %FALSE otherwise. |
476 | | */ |
477 | | gboolean |
478 | | g_value_type_transformable (GType src_type, |
479 | | GType dest_type) |
480 | 0 | { |
481 | 0 | g_return_val_if_fail (src_type, FALSE); |
482 | 0 | g_return_val_if_fail (dest_type, FALSE); |
483 | | |
484 | 0 | return (g_value_type_compatible (src_type, dest_type) || |
485 | 0 | transform_func_lookup (src_type, dest_type) != NULL); |
486 | 0 | } |
487 | | |
488 | | /** |
489 | | * g_value_type_compatible: |
490 | | * @src_type: source type to be copied. |
491 | | * @dest_type: destination type for copying. |
492 | | * |
493 | | * Returns whether a #GValue of type @src_type can be copied into |
494 | | * a #GValue of type @dest_type. |
495 | | * |
496 | | * Returns: %TRUE if g_value_copy() is possible with @src_type and @dest_type. |
497 | | */ |
498 | | gboolean |
499 | | g_value_type_compatible (GType src_type, |
500 | | GType dest_type) |
501 | 0 | { |
502 | 0 | g_return_val_if_fail (src_type, FALSE); |
503 | 0 | g_return_val_if_fail (dest_type, FALSE); |
504 | | |
505 | | /* Fast path */ |
506 | 0 | if (src_type == dest_type) |
507 | 0 | return TRUE; |
508 | | |
509 | 0 | return (g_type_is_a (src_type, dest_type) && |
510 | 0 | g_type_value_table_peek (dest_type) == g_type_value_table_peek (src_type)); |
511 | 0 | } |
512 | | |
513 | | /** |
514 | | * g_value_transform: |
515 | | * @src_value: Source value. |
516 | | * @dest_value: Target value. |
517 | | * |
518 | | * Tries to cast the contents of @src_value into a type appropriate |
519 | | * to store in @dest_value, e.g. to transform a %G_TYPE_INT value |
520 | | * into a %G_TYPE_FLOAT value. Performing transformations between |
521 | | * value types might incur precision lossage. Especially |
522 | | * transformations into strings might reveal seemingly arbitrary |
523 | | * results and shouldn't be relied upon for production code (such |
524 | | * as rcfile value or object property serialization). |
525 | | * |
526 | | * Returns: Whether a transformation rule was found and could be applied. |
527 | | * Upon failing transformations, @dest_value is left untouched. |
528 | | */ |
529 | | gboolean |
530 | | g_value_transform (const GValue *src_value, |
531 | | GValue *dest_value) |
532 | 0 | { |
533 | 0 | GType dest_type; |
534 | |
|
535 | 0 | g_return_val_if_fail (src_value, FALSE); |
536 | 0 | g_return_val_if_fail (dest_value, FALSE); |
537 | | |
538 | 0 | dest_type = G_VALUE_TYPE (dest_value); |
539 | 0 | if (g_value_type_compatible (G_VALUE_TYPE (src_value), dest_type)) |
540 | 0 | { |
541 | 0 | g_value_copy (src_value, dest_value); |
542 | | |
543 | 0 | return TRUE; |
544 | 0 | } |
545 | 0 | else |
546 | 0 | { |
547 | 0 | GValueTransform transform = transform_func_lookup (G_VALUE_TYPE (src_value), dest_type); |
548 | |
|
549 | 0 | if (transform) |
550 | 0 | { |
551 | 0 | g_value_unset (dest_value); |
552 | | |
553 | | /* setup and transform */ |
554 | 0 | value_meminit (dest_value, dest_type); |
555 | 0 | transform (src_value, dest_value); |
556 | | |
557 | 0 | return TRUE; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | return FALSE; |
561 | 0 | } |