=== Released 2.2.0 ===
[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   GBoxedCopyFunc copy;
34   GBoxedFreeFunc free;
35 } BoxedNode;
36
37
38 /* --- prototypes --- */
39 static gint     boxed_nodes_cmp         (gconstpointer  p1,
40                                          gconstpointer  p2);
41
42
43 /* --- variables --- */
44 static GBSearchArray *boxed_bsa = NULL;
45 static GBSearchConfig boxed_bconfig = G_STATIC_BCONFIG (sizeof (BoxedNode), boxed_nodes_cmp, 0);
46
47
48 /* --- functions --- */
49 static gint
50 boxed_nodes_cmp (gconstpointer p1,
51                  gconstpointer p2)
52 {
53   const BoxedNode *node1 = p1, *node2 = p2;
54
55   return G_BSEARCH_ARRAY_CMP (node1->type, node2->type);
56 }
57
58 static inline void              /* keep this function in sync with gvalue.c */
59 value_meminit (GValue *value,
60                GType   value_type)
61 {
62   value->g_type = value_type;
63   memset (value->data, 0, sizeof (value->data));
64 }
65
66 static gpointer
67 value_copy (gpointer boxed)
68 {
69   const GValue *src_value = boxed;
70   GValue *dest_value = g_new0 (GValue, 1);
71
72   if (G_VALUE_TYPE (src_value))
73     {
74       g_value_init (dest_value, G_VALUE_TYPE (src_value));
75       g_value_copy (src_value, dest_value);
76     }
77   return dest_value;
78 }
79
80 static void
81 value_free (gpointer boxed)
82 {
83   GValue *value = boxed;
84
85   if (G_VALUE_TYPE (value))
86     g_value_unset (value);
87   g_free (value);
88 }
89
90 static gpointer
91 gstring_copy (gpointer boxed)
92 {
93   const GString *src_gstring = boxed;
94
95   return g_string_new_len (src_gstring->str, src_gstring->len);
96 }
97
98 static void
99 gstring_free (gpointer boxed)
100 {
101   GString *gstring = boxed;
102
103   g_string_free (gstring, TRUE);
104 }
105
106 void
107 g_boxed_type_init (void)  /* sync with gtype.c */
108 {
109   static const GTypeInfo info = {
110     0,                          /* class_size */
111     NULL,                       /* base_init */
112     NULL,                       /* base_destroy */
113     NULL,                       /* class_init */
114     NULL,                       /* class_destroy */
115     NULL,                       /* class_data */
116     0,                          /* instance_size */
117     0,                          /* n_preallocs */
118     NULL,                       /* instance_init */
119     NULL,                       /* value_table */
120   };
121   const GTypeFundamentalInfo finfo = { G_TYPE_FLAG_DERIVABLE, };
122   GType type;
123
124   boxed_bsa = g_bsearch_array_new (&boxed_bconfig);
125
126   /* G_TYPE_BOXED
127    */
128   type = g_type_register_fundamental (G_TYPE_BOXED, "GBoxed", &info, &finfo,
129                                       G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT);
130   g_assert (type == G_TYPE_BOXED);
131 }
132
133 GType
134 g_closure_get_type (void)
135 {
136   static GType type_id = 0;
137
138   if (!type_id)
139     type_id = g_boxed_type_register_static ("GClosure",
140                                             (GBoxedCopyFunc) g_closure_ref,
141                                             (GBoxedFreeFunc) g_closure_unref);
142   return type_id;
143 }
144
145 GType
146 g_value_get_type (void)
147 {
148   static GType type_id = 0;
149
150   if (!type_id)
151     type_id = g_boxed_type_register_static ("GValue",
152                                             value_copy,
153                                             value_free);
154   return type_id;
155 }
156
157 GType
158 g_value_array_get_type (void)
159 {
160   static GType type_id = 0;
161
162   if (!type_id)
163     type_id = g_boxed_type_register_static ("GValueArray",
164                                             (GBoxedCopyFunc) g_value_array_copy,
165                                             (GBoxedFreeFunc) g_value_array_free);
166   return type_id;
167 }
168
169 GType
170 g_gstring_get_type (void)
171 {
172   static GType type_id = 0;
173
174   if (!type_id)
175     type_id = g_boxed_type_register_static ("GString",  /* the naming is a bit odd, but GString is obviously not G_TYPE_STRING */
176                                             gstring_copy,
177                                             gstring_free);
178   return type_id;
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, &boxed_bconfig, &key);
188   value->data[0].v_pointer = 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, &boxed_bconfig, &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, &boxed_bconfig, &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, &boxed_bconfig, &key);
236
237   if (!collect_values[0].v_pointer)
238     value->data[0].v_pointer = NULL;
239   else
240     {
241       if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
242         {
243           value->data[0].v_pointer = collect_values[0].v_pointer;
244           value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
245         }
246       else
247         value->data[0].v_pointer = node->copy (collect_values[0].v_pointer);
248     }
249
250   return NULL;
251 }
252
253 static gchar*
254 boxed_proxy_lcopy_value (const GValue *value,
255                          guint         n_collect_values,
256                          GTypeCValue  *collect_values,
257                          guint         collect_flags)
258 {
259   gpointer *boxed_p = collect_values[0].v_pointer;
260
261   if (!boxed_p)
262     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
263
264   if (!value->data[0].v_pointer)
265     *boxed_p = NULL;
266   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
267     *boxed_p = value->data[0].v_pointer;
268   else
269     {
270       BoxedNode key, *node;
271
272       key.type = G_VALUE_TYPE (value);
273       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
274       *boxed_p = node->copy (value->data[0].v_pointer);
275     }
276
277   return NULL;
278 }
279
280 GType
281 g_boxed_type_register_static (const gchar   *name,
282                               GBoxedCopyFunc boxed_copy,
283                               GBoxedFreeFunc boxed_free)
284 {
285   static const GTypeValueTable vtable = {
286     boxed_proxy_value_init,
287     boxed_proxy_value_free,
288     boxed_proxy_value_copy,
289     boxed_proxy_value_peek_pointer,
290     "p",
291     boxed_proxy_collect_value,
292     "p",
293     boxed_proxy_lcopy_value,
294   };
295   static const GTypeInfo type_info = {
296     0,                  /* class_size */
297     NULL,               /* base_init */
298     NULL,               /* base_finalize */
299     NULL,               /* class_init */
300     NULL,               /* class_finalize */
301     NULL,               /* class_data */
302     0,                  /* instance_size */
303     0,                  /* n_preallocs */
304     NULL,               /* instance_init */
305     &vtable,            /* value_table */
306   };
307   GType type;
308
309   g_return_val_if_fail (name != NULL, 0);
310   g_return_val_if_fail (boxed_copy != NULL, 0);
311   g_return_val_if_fail (boxed_free != NULL, 0);
312   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
313
314   type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
315
316   /* install proxy functions upon successfull registration */
317   if (type)
318     {
319       BoxedNode key;
320
321       key.type = type;
322       key.copy = boxed_copy;
323       key.free = boxed_free;
324       boxed_bsa = g_bsearch_array_insert (boxed_bsa, &boxed_bconfig, &key, TRUE);
325     }
326
327   return type;
328 }
329
330 gpointer
331 g_boxed_copy (GType         boxed_type,
332               gconstpointer src_boxed)
333 {
334   GTypeValueTable *value_table;
335   gpointer dest_boxed;
336
337   g_return_val_if_fail (G_TYPE_IS_BOXED (boxed_type), NULL);
338   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE, NULL);
339   g_return_val_if_fail (src_boxed != NULL, NULL);
340
341   value_table = g_type_value_table_peek (boxed_type);
342   if (!value_table)
343     g_return_val_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type), NULL);
344
345   /* check if our proxying implementation is used, we can short-cut here */
346   if (value_table->value_copy == boxed_proxy_value_copy)
347     {
348       BoxedNode key, *node;
349
350       key.type = boxed_type;
351       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
352       dest_boxed = node->copy ((gpointer) src_boxed);
353     }
354   else
355     {
356       GValue src_value, dest_value;
357       
358       /* we heavily rely on third-party boxed type value vtable
359        * implementations to follow normal boxed value storage
360        * (data[0].v_pointer is the boxed struct, and
361        * data[1].v_uint holds the G_VALUE_NOCOPY_CONTENTS flag,
362        * rest zero).
363        * but then, we can expect that since we layed out the
364        * g_boxed_*() API.
365        * data[1].v_uint&G_VALUE_NOCOPY_CONTENTS shouldn't be set
366        * after a copy.
367        */
368       /* equiv. to g_value_set_static_boxed() */
369       value_meminit (&src_value, boxed_type);
370       src_value.data[0].v_pointer = (gpointer) src_boxed;
371       src_value.data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
372
373       /* call third-party code copy function, fingers-crossed */
374       value_meminit (&dest_value, boxed_type);
375       value_table->value_copy (&src_value, &dest_value);
376
377       /* double check and grouse if things went wrong */
378       if (dest_value.data[1].v_ulong)
379         g_warning ("the copy_value() implementation of type `%s' seems to make use of reserved GValue fields",
380                    g_type_name (boxed_type));
381
382       dest_boxed = dest_value.data[0].v_pointer;
383     }
384
385   return dest_boxed;
386 }
387
388 void
389 g_boxed_free (GType    boxed_type,
390               gpointer boxed)
391 {
392   GTypeValueTable *value_table;
393
394   g_return_if_fail (G_TYPE_IS_BOXED (boxed_type));
395   g_return_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE);
396   g_return_if_fail (boxed != NULL);
397
398   value_table = g_type_value_table_peek (boxed_type);
399   if (!value_table)
400     g_return_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type));
401
402   /* check if our proxying implementation is used, we can short-cut here */
403   if (value_table->value_free == boxed_proxy_value_free)
404     {
405       BoxedNode key, *node;
406
407       key.type = boxed_type;
408       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
409       node->free (boxed);
410     }
411   else
412     {
413       GValue value;
414
415       /* see g_boxed_copy() on why we think we can do this */
416       value_meminit (&value, boxed_type);
417       value.data[0].v_pointer = boxed;
418       value_table->value_free (&value);
419     }
420 }
421
422 gpointer
423 g_value_get_boxed (const GValue *value)
424 {
425   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
426   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
427
428   return value->data[0].v_pointer;
429 }
430
431 gpointer
432 g_value_dup_boxed (const GValue *value)
433 {
434   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
435   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
436
437   return value->data[0].v_pointer ? g_boxed_copy (G_VALUE_TYPE (value), value->data[0].v_pointer) : NULL;
438 }
439
440 static inline void
441 value_set_boxed_internal (GValue       *value,
442                           gconstpointer const_boxed,
443                           gboolean      need_copy,
444                           gboolean      need_free)
445 {
446   BoxedNode key, *node;
447   gpointer boxed = (gpointer) const_boxed;
448
449   if (!boxed)
450     {
451       /* just resetting to NULL might not be desired, need to
452        * have value reinitialized also (for values defaulting
453        * to other default value states than a NULL data pointer),
454        * g_value_reset() will handle this
455        */
456       g_value_reset (value);
457       return;
458     }
459
460   key.type = G_VALUE_TYPE (value);
461   node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
462
463   if (node)
464     {
465       /* we proxy this type, free contents and copy right away */
466       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
467         node->free (value->data[0].v_pointer);
468       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
469       value->data[0].v_pointer = need_copy ? node->copy (boxed) : boxed;
470     }
471   else
472     {
473       /* we don't handle this type, free contents and let g_boxed_copy()
474        * figure what's required
475        */
476       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
477         g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
478       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
479       value->data[0].v_pointer = need_copy ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : boxed;
480     }
481 }
482
483 void
484 g_value_set_boxed (GValue       *value,
485                    gconstpointer boxed)
486 {
487   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
488   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
489
490   value_set_boxed_internal (value, boxed, TRUE, TRUE);
491 }
492
493 void
494 g_value_set_static_boxed (GValue       *value,
495                           gconstpointer boxed)
496 {
497   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
498   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
499
500   value_set_boxed_internal (value, boxed, FALSE, FALSE);
501 }
502
503 void
504 g_value_set_boxed_take_ownership (GValue       *value,
505                                   gconstpointer boxed)
506 {
507   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
508   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
509
510   value_set_boxed_internal (value, boxed, FALSE, TRUE);
511 }