source: trunk/third/glib2/gobject/gboxed.c @ 20721

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