removed archaic gpointer derived_data; relict and added a GData member
[platform/upstream/glib.git] / gobject / gboxed.c
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2  * Copyright (C) 2000-2001 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include        "gboxed.h"
20
21 #include        "gbsearcharray.h"
22 #include        "gvalue.h"
23 #include        "gvaluearray.h"
24 #include        "gclosure.h"
25 #include        "gvaluecollector.h"
26
27 #include <string.h>
28
29 /* --- typedefs & structures --- */
30 typedef struct
31 {
32   GType          type;
33   GBoxedInitFunc init;
34   GBoxedCopyFunc copy;
35   GBoxedFreeFunc free;
36   gboolean       is_refcounted;
37 } BoxedNode;
38
39
40 /* --- prototypes --- */
41 static gint     boxed_nodes_cmp         (gconstpointer  p1,
42                                          gconstpointer  p2);
43
44
45 /* --- variables --- */
46 static GBSearchArray boxed_bsa = G_STATIC_BSEARCH_ARRAY_INIT (sizeof (BoxedNode), boxed_nodes_cmp, 0);
47
48
49 /* --- functions --- */
50 static gint
51 boxed_nodes_cmp (gconstpointer p1,
52                  gconstpointer p2)
53 {
54   const BoxedNode *node1 = p1, *node2 = p2;
55
56   return G_BSEARCH_ARRAY_CMP (node1->type, node2->type);
57 }
58
59 static inline void              /* keep this function in sync with gvalue.c */
60 value_meminit (GValue *value,
61                GType   value_type)
62 {
63   value->g_type = value_type;
64   memset (value->data, 0, sizeof (value->data));
65 }
66
67 static gpointer
68 value_copy (gpointer boxed)
69 {
70   const GValue *src_value = boxed;
71   GValue *dest_value = g_new0 (GValue, 1);
72
73   if (G_VALUE_TYPE (src_value))
74     {
75       g_value_init (dest_value, G_VALUE_TYPE (src_value));
76       g_value_copy (src_value, dest_value);
77     }
78   return dest_value;
79 }
80
81 static void
82 value_free (gpointer boxed)
83 {
84   GValue *value = boxed;
85
86   if (G_VALUE_TYPE (value))
87     g_value_unset (value);
88   g_free (value);
89 }
90
91 static gpointer
92 value_array_init (void)
93 {
94   return g_value_array_new (0);
95 }
96
97 static gpointer
98 gstring_init (void)
99 {
100   return g_string_new ("");
101 }
102
103 static gpointer
104 gstring_copy (gpointer boxed)
105 {
106   const GString *src_gstring = boxed;
107
108   return g_string_new_len (src_gstring->str, src_gstring->len);
109 }
110
111 static void
112 gstring_free (gpointer boxed)
113 {
114   GString *gstring = boxed;
115
116   g_string_free (gstring, TRUE);
117 }
118
119 void
120 g_boxed_type_init (void)  /* sync with gtype.c */
121 {
122   static const GTypeInfo info = {
123     0,                          /* class_size */
124     NULL,                       /* base_init */
125     NULL,                       /* base_destroy */
126     NULL,                       /* class_init */
127     NULL,                       /* class_destroy */
128     NULL,                       /* class_data */
129     0,                          /* instance_size */
130     0,                          /* n_preallocs */
131     NULL,                       /* instance_init */
132     NULL,                       /* value_table */
133   };
134   const GTypeFundamentalInfo finfo = { G_TYPE_FLAG_DERIVABLE, };
135   GType type;
136
137   /* G_TYPE_BOXED
138    */
139   type = g_type_register_fundamental (G_TYPE_BOXED, "GBoxed", &info, &finfo,
140                                       G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT);
141   g_assert (type == G_TYPE_BOXED);
142
143   /* boxed: G_TYPE_CLOSURE
144    */
145   type = g_boxed_type_register_static ("GClosure",
146                                        (GBoxedInitFunc) NULL,
147                                        (GBoxedCopyFunc) g_closure_ref,
148                                        (GBoxedFreeFunc) g_closure_unref,
149                                        TRUE);
150   g_assert (type == G_TYPE_CLOSURE);
151
152   /* boxed: G_TYPE_VALUE
153    */
154   type = g_boxed_type_register_static ("GValue",
155                                        (GBoxedInitFunc) NULL,
156                                        value_copy,
157                                        value_free,
158                                        FALSE);
159   g_assert (type == G_TYPE_VALUE);
160
161   /* boxed: G_TYPE_VALUE_ARRAY
162    */
163   type = g_boxed_type_register_static ("GValueArray",
164                                        value_array_init,        /* don't allow NULL values */
165                                        (GBoxedCopyFunc) g_value_array_copy,
166                                        (GBoxedFreeFunc) g_value_array_free,
167                                        FALSE);
168   g_assert (type == G_TYPE_VALUE_ARRAY);
169
170   /* boxed: G_TYPE_GSTRING
171    * yes, the naming is a bit odd, but GString is obviously not G_TYPE_STRING
172    */
173   type = g_boxed_type_register_static ("GString",
174                                        gstring_init,            /* don't allow NULL values */
175                                        gstring_copy,
176                                        gstring_free,
177                                        FALSE);
178   g_assert (type == G_TYPE_GSTRING);
179 }
180
181 static void
182 boxed_proxy_value_init (GValue *value)
183 {
184   BoxedNode key, *node;
185
186   key.type = G_VALUE_TYPE (value);
187   node = g_bsearch_array_lookup (&boxed_bsa, &key);
188   value->data[0].v_pointer = node->init ? node->init () : NULL;
189 }
190
191 static void
192 boxed_proxy_value_free (GValue *value)
193 {
194   if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
195     {
196       BoxedNode key, *node;
197
198       key.type = G_VALUE_TYPE (value);
199       node = g_bsearch_array_lookup (&boxed_bsa, &key);
200       node->free (value->data[0].v_pointer);
201     }
202 }
203
204 static void
205 boxed_proxy_value_copy (const GValue *src_value,
206                         GValue       *dest_value)
207 {
208   if (src_value->data[0].v_pointer)
209     {
210       BoxedNode key, *node;
211
212       key.type = G_VALUE_TYPE (src_value);
213       node = g_bsearch_array_lookup (&boxed_bsa, &key);
214       dest_value->data[0].v_pointer = node->copy (src_value->data[0].v_pointer);
215     }
216   else
217     dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
218 }
219
220 static gpointer
221 boxed_proxy_value_peek_pointer (const GValue *value)
222 {
223   return value->data[0].v_pointer;
224 }
225
226 static gchar*
227 boxed_proxy_collect_value (GValue      *value,
228                            guint        n_collect_values,
229                            GTypeCValue *collect_values,
230                            guint        collect_flags)
231 {
232   BoxedNode key, *node;
233
234   key.type = G_VALUE_TYPE (value);
235   node = g_bsearch_array_lookup (&boxed_bsa, &key);
236
237   /* for NULL values, we have to call GBoxedInitFunc */
238   if (!collect_values[0].v_pointer)
239     value->data[0].v_pointer = node->init ? node->init () : NULL;
240   else
241     {
242       /* never honour G_VALUE_NOCOPY_CONTENTS for ref-counted types */
243       if (!node->is_refcounted && (collect_flags & G_VALUE_NOCOPY_CONTENTS))
244         {
245           value->data[0].v_pointer = collect_values[0].v_pointer;
246           value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
247         }
248       else
249         value->data[0].v_pointer = node->copy (collect_values[0].v_pointer);
250     }
251
252   return NULL;
253 }
254
255 static gchar*
256 boxed_proxy_lcopy_value (const GValue *value,
257                          guint         n_collect_values,
258                          GTypeCValue  *collect_values,
259                          guint         collect_flags)
260 {
261   gpointer *boxed_p = collect_values[0].v_pointer;
262
263   if (!boxed_p)
264     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
265
266   if (!value->data[0].v_pointer)
267     *boxed_p = NULL;
268   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
269     *boxed_p = value->data[0].v_pointer;
270   else
271     {
272       BoxedNode key, *node;
273
274       key.type = G_VALUE_TYPE (value);
275       node = g_bsearch_array_lookup (&boxed_bsa, &key);
276       *boxed_p = node->copy (value->data[0].v_pointer);
277     }
278
279   return NULL;
280 }
281
282 GType
283 g_boxed_type_register_static (const gchar   *name,
284                               GBoxedInitFunc boxed_init,
285                               GBoxedCopyFunc boxed_copy,
286                               GBoxedFreeFunc boxed_free,
287                               gboolean       is_refcounted)
288 {
289   static const GTypeValueTable vtable = {
290     boxed_proxy_value_init,
291     boxed_proxy_value_free,
292     boxed_proxy_value_copy,
293     boxed_proxy_value_peek_pointer,
294     "p",
295     boxed_proxy_collect_value,
296     "p",
297     boxed_proxy_lcopy_value,
298   };
299   static const GTypeInfo type_info = {
300     0,                  /* class_size */
301     NULL,               /* base_init */
302     NULL,               /* base_finalize */
303     NULL,               /* class_init */
304     NULL,               /* class_finalize */
305     NULL,               /* class_data */
306     0,                  /* instance_size */
307     0,                  /* n_preallocs */
308     NULL,               /* instance_init */
309     &vtable,            /* value_table */
310   };
311   GType type;
312
313   g_return_val_if_fail (name != NULL, 0);
314   g_return_val_if_fail (boxed_copy != NULL, 0);
315   g_return_val_if_fail (boxed_free != NULL, 0);
316   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
317
318   type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
319
320   /* install proxy functions upon successfull registration */
321   if (type)
322     {
323       BoxedNode key;
324
325       key.type = type;
326       key.init = boxed_init;
327       key.copy = boxed_copy;
328       key.free = boxed_free;
329       key.is_refcounted = is_refcounted != FALSE;
330       g_bsearch_array_insert (&boxed_bsa, &key, TRUE);
331     }
332
333   return type;
334 }
335
336 GBoxed*
337 g_boxed_copy (GType         boxed_type,
338               gconstpointer src_boxed)
339 {
340   GTypeValueTable *value_table;
341   gpointer dest_boxed;
342
343   g_return_val_if_fail (G_TYPE_IS_BOXED (boxed_type), NULL);
344   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE, NULL);
345   g_return_val_if_fail (src_boxed != NULL, NULL);
346
347   value_table = g_type_value_table_peek (boxed_type);
348   if (!value_table)
349     g_return_val_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type), NULL);
350
351   /* check if our proxying implementation is used, we can short-cut here */
352   if (value_table->value_copy == boxed_proxy_value_copy)
353     {
354       BoxedNode key, *node;
355
356       key.type = boxed_type;
357       node = g_bsearch_array_lookup (&boxed_bsa, &key);
358       dest_boxed = node->copy ((gpointer) src_boxed);
359     }
360   else
361     {
362       GValue src_value, dest_value;
363       
364       /* we heavil rely on third-party boxed type value vtable
365        * implementations to follow normal boxed value storage
366        * (data[0].v_pointer is the boxed struct, and
367        * data[1].v_uint holds the G_VALUE_NOCOPY_CONTENTS flag,
368        * rest zero).
369        * but then, we can expect that since we layed out the
370        * g_boxed_*() API.
371        * data[1].v_uint&G_VALUE_NOCOPY_CONTENTS shouldn't be set
372        * after a copy.
373        */
374       /* equiv. to g_value_set_static_boxed() */
375       value_meminit (&src_value, boxed_type);
376       src_value.data[0].v_pointer = (gpointer) src_boxed;
377       src_value.data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
378
379       /* call third-party code copy fucntion, fingers-crossed */
380       value_meminit (&dest_value, boxed_type);
381       value_table->value_copy (&src_value, &dest_value);
382
383       /* double check and grouse if things went wrong */
384       if (dest_value.data[1].v_ulong ||
385           dest_value.data[2].v_ulong ||
386           dest_value.data[3].v_ulong)
387         g_warning ("the copy_value() implementation of type `%s' seems to make use of reserved GValue fields",
388                    g_type_name (boxed_type));
389
390       dest_boxed = dest_value.data[0].v_pointer;
391     }
392
393   return dest_boxed;
394 }
395
396 void
397 g_boxed_free (GType    boxed_type,
398               gpointer boxed)
399 {
400   GTypeValueTable *value_table;
401
402   g_return_if_fail (G_TYPE_IS_BOXED (boxed_type));
403   g_return_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE);
404   g_return_if_fail (boxed != NULL);
405
406   value_table = g_type_value_table_peek (boxed_type);
407   if (!value_table)
408     g_return_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type));
409
410   /* check if our proxying implementation is used, we can short-cut here */
411   if (value_table->value_free == boxed_proxy_value_free)
412     {
413       BoxedNode key, *node;
414
415       key.type = boxed_type;
416       node = g_bsearch_array_lookup (&boxed_bsa, &key);
417       node->free (boxed);
418     }
419   else
420     {
421       GValue value;
422
423       /* see g_boxed_copy() on why we think we can do this */
424       value_meminit (&value, boxed_type);
425       value.data[0].v_pointer = boxed;
426       value_table->value_free (&value);
427     }
428 }
429
430 gpointer
431 g_value_get_boxed (const GValue *value)
432 {
433   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
434   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
435
436   return value->data[0].v_pointer;
437 }
438
439 gpointer
440 g_value_dup_boxed (GValue *value)
441 {
442   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
443   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
444
445   return value->data[0].v_pointer ? g_boxed_copy (G_VALUE_TYPE (value), value->data[0].v_pointer) : NULL;
446 }
447
448 static inline void
449 value_set_boxed_internal (GValue       *value,
450                           gconstpointer const_boxed,
451                           gboolean      need_copy,
452                           gboolean      need_free)
453 {
454   BoxedNode key, *node;
455   gpointer boxed = (gpointer) const_boxed;
456
457   if (!boxed)
458     {
459       /* just resetting to NULL might not be desired, need to
460        * have value reinitialized also (for values defaulting
461        * to other default value states than a NULL data pointer),
462        * g_value_reset() will handle this
463        */
464       g_value_reset (value);
465       return;
466     }
467
468   key.type = G_VALUE_TYPE (value);
469   node = g_bsearch_array_lookup (&boxed_bsa, &key);
470
471   if (node)
472     {
473       /* we proxy this type, free contents and copy right away */
474       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
475         node->free (value->data[0].v_pointer);
476       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
477       value->data[0].v_pointer = need_copy ? node->copy (boxed) : boxed;
478     }
479   else
480     {
481       /* we don't handle this type, free contents and let g_boxed_copy()
482        * figure what's required
483        */
484       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
485         g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
486       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
487       value->data[0].v_pointer = need_copy ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : boxed;
488     }
489 }
490
491 void
492 g_value_set_boxed (GValue       *value,
493                    gconstpointer boxed)
494 {
495   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
496   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
497
498   value_set_boxed_internal (value, boxed, TRUE, TRUE);
499 }
500
501 void
502 g_value_set_static_boxed (GValue       *value,
503                           gconstpointer boxed)
504 {
505   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
506   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
507
508   value_set_boxed_internal (value, boxed, FALSE, FALSE);
509 }
510
511 void
512 g_value_set_boxed_take_ownership (GValue       *value,
513                                   gconstpointer boxed)
514 {
515   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
516   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
517
518   value_set_boxed_internal (value, boxed, FALSE, TRUE);
519 }