Added GRegex boxed type. (#445065, Carlos Garnacho)
[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 GType
245 g_regex_get_type (void)
246 {
247   static GType type_id = 0;
248   if (!type_id)
249     type_id = g_boxed_type_register_static (g_intern_static_string ("GRegex"),
250                                             (GBoxedCopyFunc) g_regex_ref,
251                                             (GBoxedFreeFunc) g_regex_unref);
252   return type_id;
253 }
254
255 static void
256 boxed_proxy_value_init (GValue *value)
257 {
258   value->data[0].v_pointer = NULL;
259 }
260
261 static void
262 boxed_proxy_value_free (GValue *value)
263 {
264   if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
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       node->free (value->data[0].v_pointer);
271     }
272 }
273
274 static void
275 boxed_proxy_value_copy (const GValue *src_value,
276                         GValue       *dest_value)
277 {
278   if (src_value->data[0].v_pointer)
279     {
280       BoxedNode key, *node;
281
282       key.type = G_VALUE_TYPE (src_value);
283       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
284       dest_value->data[0].v_pointer = node->copy (src_value->data[0].v_pointer);
285     }
286   else
287     dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
288 }
289
290 static gpointer
291 boxed_proxy_value_peek_pointer (const GValue *value)
292 {
293   return value->data[0].v_pointer;
294 }
295
296 static gchar*
297 boxed_proxy_collect_value (GValue      *value,
298                            guint        n_collect_values,
299                            GTypeCValue *collect_values,
300                            guint        collect_flags)
301 {
302   BoxedNode key, *node;
303
304   key.type = G_VALUE_TYPE (value);
305   node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
306
307   if (!collect_values[0].v_pointer)
308     value->data[0].v_pointer = NULL;
309   else
310     {
311       if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
312         {
313           value->data[0].v_pointer = collect_values[0].v_pointer;
314           value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
315         }
316       else
317         value->data[0].v_pointer = node->copy (collect_values[0].v_pointer);
318     }
319
320   return NULL;
321 }
322
323 static gchar*
324 boxed_proxy_lcopy_value (const GValue *value,
325                          guint         n_collect_values,
326                          GTypeCValue  *collect_values,
327                          guint         collect_flags)
328 {
329   gpointer *boxed_p = collect_values[0].v_pointer;
330
331   if (!boxed_p)
332     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
333
334   if (!value->data[0].v_pointer)
335     *boxed_p = NULL;
336   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
337     *boxed_p = value->data[0].v_pointer;
338   else
339     {
340       BoxedNode key, *node;
341
342       key.type = G_VALUE_TYPE (value);
343       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
344       *boxed_p = node->copy (value->data[0].v_pointer);
345     }
346
347   return NULL;
348 }
349
350 GType
351 g_boxed_type_register_static (const gchar   *name,
352                               GBoxedCopyFunc boxed_copy,
353                               GBoxedFreeFunc boxed_free)
354 {
355   static const GTypeValueTable vtable = {
356     boxed_proxy_value_init,
357     boxed_proxy_value_free,
358     boxed_proxy_value_copy,
359     boxed_proxy_value_peek_pointer,
360     "p",
361     boxed_proxy_collect_value,
362     "p",
363     boxed_proxy_lcopy_value,
364   };
365   static const GTypeInfo type_info = {
366     0,                  /* class_size */
367     NULL,               /* base_init */
368     NULL,               /* base_finalize */
369     NULL,               /* class_init */
370     NULL,               /* class_finalize */
371     NULL,               /* class_data */
372     0,                  /* instance_size */
373     0,                  /* n_preallocs */
374     NULL,               /* instance_init */
375     &vtable,            /* value_table */
376   };
377   GType type;
378
379   g_return_val_if_fail (name != NULL, 0);
380   g_return_val_if_fail (boxed_copy != NULL, 0);
381   g_return_val_if_fail (boxed_free != NULL, 0);
382   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
383
384   type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
385
386   /* install proxy functions upon successfull registration */
387   if (type)
388     {
389       BoxedNode key;
390
391       key.type = type;
392       key.copy = boxed_copy;
393       key.free = boxed_free;
394       boxed_bsa = g_bsearch_array_insert (boxed_bsa, &boxed_bconfig, &key);
395     }
396
397   return type;
398 }
399
400 gpointer
401 g_boxed_copy (GType         boxed_type,
402               gconstpointer src_boxed)
403 {
404   GTypeValueTable *value_table;
405   gpointer dest_boxed;
406
407   g_return_val_if_fail (G_TYPE_IS_BOXED (boxed_type), NULL);
408   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE, NULL);
409   g_return_val_if_fail (src_boxed != NULL, NULL);
410
411   value_table = g_type_value_table_peek (boxed_type);
412   if (!value_table)
413     g_return_val_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type), NULL);
414
415   /* check if our proxying implementation is used, we can short-cut here */
416   if (value_table->value_copy == boxed_proxy_value_copy)
417     {
418       BoxedNode key, *node;
419
420       key.type = boxed_type;
421       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
422       dest_boxed = node->copy ((gpointer) src_boxed);
423     }
424   else
425     {
426       GValue src_value, dest_value;
427       
428       /* we heavily rely on third-party boxed type value vtable
429        * implementations to follow normal boxed value storage
430        * (data[0].v_pointer is the boxed struct, and
431        * data[1].v_uint holds the G_VALUE_NOCOPY_CONTENTS flag,
432        * rest zero).
433        * but then, we can expect that since we layed out the
434        * g_boxed_*() API.
435        * data[1].v_uint&G_VALUE_NOCOPY_CONTENTS shouldn't be set
436        * after a copy.
437        */
438       /* equiv. to g_value_set_static_boxed() */
439       value_meminit (&src_value, boxed_type);
440       src_value.data[0].v_pointer = (gpointer) src_boxed;
441       src_value.data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
442
443       /* call third-party code copy function, fingers-crossed */
444       value_meminit (&dest_value, boxed_type);
445       value_table->value_copy (&src_value, &dest_value);
446
447       /* double check and grouse if things went wrong */
448       if (dest_value.data[1].v_ulong)
449         g_warning ("the copy_value() implementation of type `%s' seems to make use of reserved GValue fields",
450                    g_type_name (boxed_type));
451
452       dest_boxed = dest_value.data[0].v_pointer;
453     }
454
455   return dest_boxed;
456 }
457
458 void
459 g_boxed_free (GType    boxed_type,
460               gpointer boxed)
461 {
462   GTypeValueTable *value_table;
463
464   g_return_if_fail (G_TYPE_IS_BOXED (boxed_type));
465   g_return_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE);
466   g_return_if_fail (boxed != NULL);
467
468   value_table = g_type_value_table_peek (boxed_type);
469   if (!value_table)
470     g_return_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type));
471
472   /* check if our proxying implementation is used, we can short-cut here */
473   if (value_table->value_free == boxed_proxy_value_free)
474     {
475       BoxedNode key, *node;
476
477       key.type = boxed_type;
478       node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
479       node->free (boxed);
480     }
481   else
482     {
483       GValue value;
484
485       /* see g_boxed_copy() on why we think we can do this */
486       value_meminit (&value, boxed_type);
487       value.data[0].v_pointer = boxed;
488       value_table->value_free (&value);
489     }
490 }
491
492 gpointer
493 g_value_get_boxed (const GValue *value)
494 {
495   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
496   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
497
498   return value->data[0].v_pointer;
499 }
500
501 gpointer
502 g_value_dup_boxed (const GValue *value)
503 {
504   g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
505   g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
506
507   return value->data[0].v_pointer ? g_boxed_copy (G_VALUE_TYPE (value), value->data[0].v_pointer) : NULL;
508 }
509
510 static inline void
511 value_set_boxed_internal (GValue       *value,
512                           gconstpointer const_boxed,
513                           gboolean      need_copy,
514                           gboolean      need_free)
515 {
516   BoxedNode key, *node;
517   gpointer boxed = (gpointer) const_boxed;
518
519   if (!boxed)
520     {
521       /* just resetting to NULL might not be desired, need to
522        * have value reinitialized also (for values defaulting
523        * to other default value states than a NULL data pointer),
524        * g_value_reset() will handle this
525        */
526       g_value_reset (value);
527       return;
528     }
529
530   key.type = G_VALUE_TYPE (value);
531   node = g_bsearch_array_lookup (boxed_bsa, &boxed_bconfig, &key);
532
533   if (node)
534     {
535       /* we proxy this type, free contents and copy right away */
536       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
537         node->free (value->data[0].v_pointer);
538       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
539       value->data[0].v_pointer = need_copy ? node->copy (boxed) : boxed;
540     }
541   else
542     {
543       /* we don't handle this type, free contents and let g_boxed_copy()
544        * figure what's required
545        */
546       if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
547         g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
548       value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
549       value->data[0].v_pointer = need_copy ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : boxed;
550     }
551 }
552
553 void
554 g_value_set_boxed (GValue       *value,
555                    gconstpointer boxed)
556 {
557   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
558   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
559
560   value_set_boxed_internal (value, boxed, TRUE, TRUE);
561 }
562
563 void
564 g_value_set_static_boxed (GValue       *value,
565                           gconstpointer boxed)
566 {
567   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
568   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
569
570   value_set_boxed_internal (value, boxed, FALSE, FALSE);
571 }
572
573 void
574 g_value_set_boxed_take_ownership (GValue       *value,
575                                   gconstpointer boxed)
576 {
577   g_value_take_boxed (value, boxed);
578 }
579
580 void
581 g_value_take_boxed (GValue       *value,
582                     gconstpointer boxed)
583 {
584   g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
585   g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
586
587   value_set_boxed_internal (value, boxed, FALSE, TRUE);
588 }
589
590 #define __G_BOXED_C__
591 #include "gobjectaliasdef.c"