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