Use <malloc.h> also with Digital Mars compiler on Win32. (#346808, Serhat
[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        "gobjectalias.h"
28
29 #include <string.h>
30
31 /* --- typedefs & structures --- */
32 typedef struct
33 {
34   GType          type;
35   GBoxedCopyFunc copy;
36   GBoxedFreeFunc free;
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 = NULL;
47 static const GBSearchConfig boxed_bconfig = {
48   sizeof (BoxedNode),
49   boxed_nodes_cmp,
50   0,
51 };
52
53
54 /* --- functions --- */
55 static gint
56 boxed_nodes_cmp (gconstpointer p1,
57                  gconstpointer p2)
58 {
59   const BoxedNode *node1 = p1, *node2 = p2;
60
61   return G_BSEARCH_ARRAY_CMP (node1->type, node2->type);
62 }
63
64 static inline void              /* keep this function in sync with gvalue.c */
65 value_meminit (GValue *value,
66                GType   value_type)
67 {
68   value->g_type = value_type;
69   memset (value->data, 0, sizeof (value->data));
70 }
71
72 static gpointer
73 value_copy (gpointer boxed)
74 {
75   const GValue *src_value = boxed;
76   GValue *dest_value = g_new0 (GValue, 1);
77
78   if (G_VALUE_TYPE (src_value))
79     {
80       g_value_init (dest_value, G_VALUE_TYPE (src_value));
81       g_value_copy (src_value, dest_value);
82     }
83   return dest_value;
84 }
85
86 static void
87 value_free (gpointer boxed)
88 {
89   GValue *value = boxed;
90
91   if (G_VALUE_TYPE (value))
92     g_value_unset (value);
93   g_free (value);
94 }
95
96 void
97 g_boxed_type_init (void) 
98 {
99   static const GTypeInfo info = {
100     0,                          /* class_size */
101     NULL,                       /* base_init */
102     NULL,                       /* base_destroy */
103     NULL,                       /* class_init */
104     NULL,                       /* class_destroy */
105     NULL,                       /* class_data */
106     0,                          /* instance_size */
107     0,                          /* n_preallocs */
108     NULL,                       /* instance_init */
109     NULL,                       /* value_table */
110   };
111   const GTypeFundamentalInfo finfo = { G_TYPE_FLAG_DERIVABLE, };
112   GType type;
113
114   boxed_bsa = g_bsearch_array_create (&boxed_bconfig);
115
116   /* G_TYPE_BOXED
117    */
118   type = g_type_register_fundamental (G_TYPE_BOXED, g_intern_static_string ("GBoxed"), &info, &finfo,
119                                       G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT);
120   g_assert (type == G_TYPE_BOXED);
121 }
122
123 GType
124 g_closure_get_type (void)
125 {
126   static GType type_id = 0;
127
128   if (!type_id)
129     type_id = g_boxed_type_register_static (g_intern_static_string ("GClosure"),
130                                             (GBoxedCopyFunc) g_closure_ref,
131                                             (GBoxedFreeFunc) g_closure_unref);
132   return type_id;
133 }
134
135 GType
136 g_value_get_type (void)
137 {
138   static GType type_id = 0;
139
140   if (!type_id)
141     type_id = g_boxed_type_register_static (g_intern_static_string ("GValue"),
142                                             value_copy,
143                                             value_free);
144   return type_id;
145 }
146
147 GType
148 g_value_array_get_type (void)
149 {
150   static GType type_id = 0;
151
152   if (!type_id)
153     type_id = g_boxed_type_register_static (g_intern_static_string ("GValueArray"),
154                                             (GBoxedCopyFunc) g_value_array_copy,
155                                             (GBoxedFreeFunc) g_value_array_free);
156   return type_id;
157 }
158
159 static gpointer
160 gdate_copy (gpointer boxed)
161 {
162   const GDate *date = (const GDate*) boxed;
163
164   return g_date_new_julian (g_date_get_julian (date));
165 }
166
167 GType
168 g_date_get_type (void)
169 {
170   static GType type_id = 0;
171
172   if (!type_id)
173     type_id = g_boxed_type_register_static (g_intern_static_string ("GDate"),
174                                             (GBoxedCopyFunc) gdate_copy,
175                                             (GBoxedFreeFunc) g_date_free);
176   return type_id;
177 }
178
179 GType
180 g_strv_get_type (void)
181 {
182   static GType type_id = 0;
183
184   if (!type_id)
185     type_id = g_boxed_type_register_static (g_intern_static_string ("GStrv"),
186                                             (GBoxedCopyFunc) g_strdupv,
187                                             (GBoxedFreeFunc) g_strfreev);
188   return type_id;
189 }
190
191 static gpointer
192 gstring_copy (gpointer boxed)
193 {
194   const GString *src_gstring = boxed;
195
196   return g_string_new_len (src_gstring->str, src_gstring->len);
197 }
198
199 static void
200 gstring_free (gpointer boxed)
201 {
202   GString *gstring = boxed;
203
204   g_string_free (gstring, TRUE);
205 }
206
207 GType
208 g_gstring_get_type (void)
209 {
210   static GType type_id = 0;
211
212   if (!type_id)
213     type_id = g_boxed_type_register_static (g_intern_static_string ("GString"),
214                                             /* the naming is a bit odd, but GString is obviously not G_TYPE_STRING */
215                                             gstring_copy,
216                                             gstring_free);
217   return type_id;
218 }
219
220 static gpointer
221 hash_table_copy (gpointer boxed)
222 {
223   GHashTable *hash_table = boxed;
224   return g_hash_table_ref (hash_table);
225 }
226
227 static void
228 hash_table_free (gpointer boxed)
229 {
230   GHashTable *hash_table = boxed;
231   g_hash_table_unref (hash_table);
232 }
233
234 GType
235 g_hash_table_get_type (void)
236 {
237   static GType type_id = 0;
238   if (!type_id)
239     type_id = g_boxed_type_register_static (g_intern_static_string ("GHashTable"),
240                                             hash_table_copy, hash_table_free);
241   return type_id;
242 }
243
244 static void
245 boxed_proxy_value_init (GValue *value)
246 {
247   value->data[0].v_pointer = NULL;
248 }
249
250 static void
251 boxed_proxy_value_free (GValue *value)
252 {
253   if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
254     {
255       BoxedNode key, *node;
256
257       key.type = G_VALUE_TYPE (value);
258       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
259       node->free (value->data[0].v_pointer);
260     }
261 }
262
263 static void
264 boxed_proxy_value_copy (const GValue *src_value,
265                         GValue       *dest_value)
266 {
267   if (src_value->data[0].v_pointer)
268     {
269       BoxedNode key, *node;
270
271       key.type = G_VALUE_TYPE (src_value);
272       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
273       dest_value->data[0].v_pointer = node->copy (src_value->data[0].v_pointer);
274     }
275   else
276     dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
277 }
278
279 static gpointer
280 boxed_proxy_value_peek_pointer (const GValue *value)
281 {
282   return value->data[0].v_pointer;
283 }
284
285 static gchar*
286 boxed_proxy_collect_value (GValue      *value,
287                            guint        n_collect_values,
288                            GTypeCValue *collect_values,
289                            guint        collect_flags)
290 {
291   BoxedNode key, *node;
292
293   key.type = G_VALUE_TYPE (value);
294   node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
295
296   if (!collect_values[0].v_pointer)
297     value->data[0].v_pointer = NULL;
298   else
299     {
300       if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
301         {
302           value->data[0].v_pointer = collect_values[0].v_pointer;
303           value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
304         }
305       else
306         value->data[0].v_pointer = node->copy (collect_values[0].v_pointer);
307     }
308
309   return NULL;
310 }
311
312 static gchar*
313 boxed_proxy_lcopy_value (const GValue *value,
314                          guint         n_collect_values,
315                          GTypeCValue  *collect_values,
316                          guint         collect_flags)
317 {
318   gpointer *boxed_p = collect_values[0].v_pointer;
319
320   if (!boxed_p)
321     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
322
323   if (!value->data[0].v_pointer)
324     *boxed_p = NULL;
325   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
326     *boxed_p = value->data[0].v_pointer;
327   else
328     {
329       BoxedNode key, *node;
330
331       key.type = G_VALUE_TYPE (value);
332       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
333       *boxed_p = node->copy (value->data[0].v_pointer);
334     }
335
336   return NULL;
337 }
338
339 GType
340 g_boxed_type_register_static (const gchar   *name,
341                               GBoxedCopyFunc boxed_copy,
342                               GBoxedFreeFunc boxed_free)
343 {
344   static const GTypeValueTable vtable = {
345     boxed_proxy_value_init,
346     boxed_proxy_value_free,
347     boxed_proxy_value_copy,
348     boxed_proxy_value_peek_pointer,
349     "p",
350     boxed_proxy_collect_value,
351     "p",
352     boxed_proxy_lcopy_value,
353   };
354   static const GTypeInfo type_info = {
355     0,                  /* class_size */
356     NULL,               /* base_init */
357     NULL,               /* base_finalize */
358     NULL,               /* class_init */
359     NULL,               /* class_finalize */
360     NULL,               /* class_data */
361     0,                  /* instance_size */
362     0,                  /* n_preallocs */
363     NULL,               /* instance_init */
364     &vtable,            /* value_table */
365   };
366   GType type;
367
368   g_return_val_if_fail (name != NULL, 0);
369   g_return_val_if_fail (boxed_copy != NULL, 0);
370   g_return_val_if_fail (boxed_free != NULL, 0);
371   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
372
373   type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
374
375   /* install proxy functions upon successfull registration */
376   if (type)
377     {
378       BoxedNode key;
379
380       key.type = type;
381       key.copy = boxed_copy;
382       key.free = boxed_free;
383       boxed_bsa = g_bsearch_array_insert (boxed_bsa, &boxed_bconfig, &key);
384     }
385
386   return type;
387 }
388
389 gpointer
390 g_boxed_copy (GType         boxed_type,
391               gconstpointer src_boxed)
392 {
393   GTypeValueTable *value_table;
394   gpointer dest_boxed;
395
396   g_return_val_if_fail (G_TYPE_IS_BOXED (boxed_type), NULL);
397   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE, NULL);
398   g_return_val_if_fail (src_boxed != NULL, NULL);
399
400   value_table = g_type_value_table_peek (boxed_type);
401   if (!value_table)
402     g_return_val_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type), NULL);
403
404   /* check if our proxying implementation is used, we can short-cut here */
405   if (value_table->value_copy == boxed_proxy_value_copy)
406     {
407       BoxedNode key, *node;
408
409       key.type = boxed_type;
410       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
411       dest_boxed = node->copy ((gpointer) src_boxed);
412     }
413   else
414     {
415       GValue src_value, dest_value;
416       
417       /* we heavily rely on third-party boxed type value vtable
418        * implementations to follow normal boxed value storage
419        * (data[0].v_pointer is the boxed struct, and
420        * data[1].v_uint holds the G_VALUE_NOCOPY_CONTENTS flag,
421        * rest zero).
422        * but then, we can expect that since we layed out the
423        * g_boxed_*() API.
424        * data[1].v_uint&G_VALUE_NOCOPY_CONTENTS shouldn't be set
425        * after a copy.
426        */
427       /* equiv. to g_value_set_static_boxed() */
428       value_meminit (&src_value, boxed_type);
429       src_value.data[0].v_pointer = (gpointer) src_boxed;
430       src_value.data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
431
432       /* call third-party code copy function, fingers-crossed */
433       value_meminit (&dest_value, boxed_type);
434       value_table->value_copy (&src_value, &dest_value);
435
436       /* double check and grouse if things went wrong */
437       if (dest_value.data[1].v_ulong)
438         g_warning ("the copy_value() implementation of type `%s' seems to make use of reserved GValue fields",
439                    g_type_name (boxed_type));
440
441       dest_boxed = dest_value.data[0].v_pointer;
442     }
443
444   return dest_boxed;
445 }
446
447 void
448 g_boxed_free (GType    boxed_type,
449               gpointer boxed)
450 {
451   GTypeValueTable *value_table;
452
453   g_return_if_fail (G_TYPE_IS_BOXED (boxed_type));
454   g_return_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE);
455   g_return_if_fail (boxed != NULL);
456
457   value_table = g_type_value_table_peek (boxed_type);
458   if (!value_table)
459     g_return_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type));
460
461   /* check if our proxying implementation is used, we can short-cut here */
462   if (value_table->value_free == boxed_proxy_value_free)
463     {
464       BoxedNode key, *node;
465
466       key.type = boxed_type;
467       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
468       node->free (boxed);
469     }
470   else
471     {
472       GValue value;
473
474       /* see g_boxed_copy() on why we think we can do this */
475       value_meminit (&value, boxed_type);
476       value.data[0].v_pointer = boxed;
477       value_table->value_free (&value);
478     }
479 }
480
481 gpointer
482 g_value_get_boxed (const GValue *value)
483 {
484   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
485   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
486
487   return value->data[0].v_pointer;
488 }
489
490 gpointer
491 g_value_dup_boxed (const GValue *value)
492 {
493   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
494   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
495
496   return value->data[0].v_pointer ? g_boxed_copy (G_VALUE_TYPE (value), value->data[0].v_pointer) : NULL;
497 }
498
499 static inline void
500 value_set_boxed_internal (GValue       *value,
501                           gconstpointer const_boxed,
502                           gboolean      need_copy,
503                           gboolean      need_free)
504 {
505   BoxedNode key, *node;
506   gpointer boxed = (gpointer) const_boxed;
507
508   if (!boxed)
509     {
510       /* just resetting to NULL might not be desired, need to
511        * have value reinitialized also (for values defaulting
512        * to other default value states than a NULL data pointer),
513        * g_value_reset() will handle this
514        */
515       g_value_reset (value);
516       return;
517     }
518
519   key.type = G_VALUE_TYPE (value);
520   node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
521
522   if (node)
523     {
524       /* we proxy this type, free contents and copy right away */
525       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
526         node->free (value->data[0].v_pointer);
527       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
528       value->data[0].v_pointer = need_copy ? node->copy (boxed) : boxed;
529     }
530   else
531     {
532       /* we don't handle this type, free contents and let g_boxed_copy()
533        * figure what's required
534        */
535       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
536         g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
537       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
538       value->data[0].v_pointer = need_copy ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : boxed;
539     }
540 }
541
542 void
543 g_value_set_boxed (GValue       *value,
544                    gconstpointer boxed)
545 {
546   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
547   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
548
549   value_set_boxed_internal (value, boxed, TRUE, TRUE);
550 }
551
552 void
553 g_value_set_static_boxed (GValue       *value,
554                           gconstpointer boxed)
555 {
556   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
557   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
558
559   value_set_boxed_internal (value, boxed, FALSE, FALSE);
560 }
561
562 void
563 g_value_set_boxed_take_ownership (GValue       *value,
564                                   gconstpointer boxed)
565 {
566   g_value_take_boxed (value, boxed);
567 }
568
569 void
570 g_value_take_boxed (GValue       *value,
571                     gconstpointer boxed)
572 {
573   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
574   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
575
576   value_set_boxed_internal (value, boxed, FALSE, TRUE);
577 }
578
579 #define __G_BOXED_C__
580 #include "gobjectaliasdef.c"