Re: dbus glib bindings: deriving from G_TYPE_BOXED, parameterized types



[I'm dropping dbus@ from the CC; however I just sent an updated patch
 there which may be interesting context for this]

On Mon, 2005-05-16 at 15:22 +0200, Tim Janik wrote:

> is this type meant to be abstract (i.e. you'd only use boxed instances
> which are truly parameterized)?

Yeah.

> > 1) Type names can't contain < or >
> 
> extending the charset for typenames s no problem. it's an arbitrary
> limitation made inside gtype.c.

Ok, that'd be nice.  Currently I'm generating type names like 
"GArray+guint" and "GHashTable+gchararray+gchararray".  I'd like to be
able to use "GArray<guint>" and "GHashTable<gchararray,gchararray>".

> > 2) We can't "deep" derive from G_TYPE_BOXED
> 
> we didn't see a use for that until now. it can be switched on, the glib code
> should just be checked for occourances of dependance on flat derivation of
> boxed types. (i don't expect them to exist though)

Right.  I am hacking around this at the moment by using qdata on the
types.

> > #define DBUS_G_TYPE_STR_STR_HASHTABLE (g_type_build_parameterized ("GHashTable<gchararray,gchararray>"))
> 
> could you outline what exactly you need besides tearing down the two
> artifical limitations you described above? (e.g. present code for
> g_type_build_parameterized(), etc.)

Indeed.  I've attached the "generic" part of the dbus work here.
There's dbus-gtype-specialized.[hc], which define an interface for both
registering and manipulating "specialized" types of collections (GArray,
GPtrArray, GList), and maps (GHashTable).

This is neat because now the dbus glib marshalling code can e.g. accept
any kind of registered collection, instead of just GArray.  For example
you could send a GList<gchararray> and that would be marshalled as a
D-BUS array, just like GArray<gchararray>.

I've also attached dbus-gvalue-utils.[hc], which register specialization
vtables for GArray, GPtrArray, and GHashTable.

To understand this, I'd look at dbus-gtype-specialized.h and get a feel
for the interface, then
dbus-gvalue-utils.c:dbus_g_type_specialized_builtins_init to see how
it's used.

/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gtype-specialized.h: Non-DBus-specific functions for specialized GTypes
 *
 * Copyright (C) 2005 Red Hat, Inc.
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef DBUS_GOBJECT_TYPE_SPECIALIZED_H
#define DBUS_GOBJECT_TYPE_SPECIALIZED_H

#include <glib.h>
#include <glib-object.h>

G_BEGIN_DECLS

GType          dbus_g_type_get_collection                   (const char *container,
							     GType       specialization);
GType          dbus_g_type_get_map                          (const char *container,
							     GType       key_specialization,
							     GType       value_specialization);
gboolean       dbus_g_type_is_collection                    (GType       gtype);
gboolean       dbus_g_type_is_map                           (GType       gtype);
GType          dbus_g_type_get_collection_specialization    (GType       gtype);
GType          dbus_g_type_get_map_key_specialization       (GType       gtype);
GType          dbus_g_type_get_map_value_specialization     (GType       gtype);

typedef void   (*DBusGTypeSpecializedCollectionIterator)    (const GValue *val,
							     gpointer      user_data);
typedef void   (*DBusGTypeSpecializedMapIterator)           (const GValue *key_val,
							     const GValue *value_val,
							     gpointer      user_data);

gpointer       dbus_g_type_specialized_construct            (GType type);

gboolean       dbus_g_type_collection_get_fixed             (GValue                                 *value,
							     gpointer                               *data,
							     guint                                  *len);

void           dbus_g_type_collection_value_iterate         (GValue                                 *value,
							     DBusGTypeSpecializedCollectionIterator  iterator,
							     gpointer                                user_data);

void           dbus_g_type_map_value_iterate                (GValue                                 *value,
							     DBusGTypeSpecializedMapIterator         iterator,
							     gpointer                                user_data);

typedef gpointer (*DBusGTypeSpecializedConstructor) (GType type);
typedef void     (*DBusGTypeSpecializedFreeFunc)    (GType type, gpointer val);
typedef gpointer (*DBusGTypeSpecializedCopyFunc)    (GType type, gpointer src);

typedef struct {
  DBusGTypeSpecializedConstructor    constructor;
  DBusGTypeSpecializedFreeFunc       free_func;
  DBusGTypeSpecializedCopyFunc       copy_func;
  gpointer                           padding1;
  gpointer                           padding2;
  gpointer                           padding3;
} DBusGTypeSpecializedVtable;

typedef gboolean (*DBusGTypeSpecializedCollectionFixedAccessorFunc) (GType type, gpointer instance, gpointer *values, guint *len);
typedef void (*DBusGTypeSpecializedCollectionIteratorFunc) (GType type, gpointer instance, DBusGTypeSpecializedCollectionIterator iterator, gpointer user_data);

typedef struct {
  DBusGTypeSpecializedVtable                        base_vtable;
  DBusGTypeSpecializedCollectionFixedAccessorFunc   fixed_accessor;
  DBusGTypeSpecializedCollectionIteratorFunc        iterator;
} DBusGTypeSpecializedCollectionVtable;

typedef void (*DBusGTypeSpecializedMapIteratorFunc) (GType type, gpointer instance, DBusGTypeSpecializedMapIterator iterator, gpointer user_data);

typedef struct {
  DBusGTypeSpecializedVtable                        base_vtable;
  DBusGTypeSpecializedMapIteratorFunc               iterator;
} DBusGTypeSpecializedMapVtable;

void           dbus_g_type_specialized_init           (void);

void           dbus_g_type_register_collection        (const char                                   *name,
						       const DBusGTypeSpecializedCollectionVtable   *vtable,
						       guint                                         flags);
  
void           dbus_g_type_register_map               (const char                                   *name,
						       const DBusGTypeSpecializedMapVtable          *vtable,
						       guint                                         flags);

G_END_DECLS

#endif
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gtype-specialized.c: Non-DBus-specific functions for specialized GTypes
 *
 * Copyright (C) 2005 Red Hat, Inc.
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "dbus-gtype-specialized.h"
#include <glib.h>
#include <string.h>
#include <gobject/gvaluecollector.h>

typedef enum {
  DBUS_G_SPECTYPE_COLLECTION,
  DBUS_G_SPECTYPE_MAP
} DBusGTypeSpecializedType;

typedef struct {
  DBusGTypeSpecializedType type;
  const DBusGTypeSpecializedVtable     *vtable;
} DBusGTypeSpecializedContainer;

typedef struct {
  GType types[6];
  const DBusGTypeSpecializedContainer     *klass;
} DBusGTypeSpecializedData;

static GHashTable /* char * -> data* */ *specialized_containers;

static GQuark
specialized_type_data_quark ()
{
  static GQuark quark;
  if (!quark)
    quark = g_quark_from_static_string ("DBusGTypeSpecializedData");
  
  return quark;
}

void
dbus_g_type_specialized_init (void)
{
  specialized_containers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}

static gboolean
specialized_types_is_initialized (void)
{
  return specialized_containers != NULL;
}

static DBusGTypeSpecializedData *
lookup_specialization_data (GType type)
{
  return g_type_get_qdata (type, specialized_type_data_quark ());
}

/* Copied from gboxed.c */
static void
proxy_value_init (GValue *value)
{
  value->data[0].v_pointer = NULL;
}

/* Adapted from gboxed.c */
static void
proxy_value_free (GValue *value)
{
  if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
    {
      DBusGTypeSpecializedData *data;
      GType type;

      type = G_VALUE_TYPE (value);
      data = lookup_specialization_data (type);
      g_assert (data != NULL);

      data->klass->vtable->free_func (type, value->data[0].v_pointer);
    }
}

/* Adapted from gboxed.c */
static void
proxy_value_copy (const GValue *src_value,
		  GValue       *dest_value)
{
  if (src_value->data[0].v_pointer)
    {
      DBusGTypeSpecializedData *data;
      GType type;
      type = G_VALUE_TYPE (src_value);
      data = lookup_specialization_data (type);
      g_assert (data != NULL);
      dest_value->data[0].v_pointer = data->klass->vtable->copy_func (type, src_value->data[0].v_pointer);
    }
  else
    dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
}

/* Copied from gboxed.c */
static gpointer
proxy_value_peek_pointer (const GValue *value)
{
  return value->data[0].v_pointer;
}

/* Adapted from gboxed.c */
static gchar*
proxy_collect_value (GValue      *value,
		     guint        n_collect_values,
		     GTypeCValue *collect_values,
		     guint        collect_flags)
{
  DBusGTypeSpecializedData *data;
  GType type;

  type = G_VALUE_TYPE (value);
  data = lookup_specialization_data (type);

  if (!collect_values[0].v_pointer)
    value->data[0].v_pointer = NULL;
  else
    {
      if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
	{
	  value->data[0].v_pointer = collect_values[0].v_pointer;
	  value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
	}
      else
	value->data[0].v_pointer = data->klass->vtable->copy_func (type, collect_values[0].v_pointer);
    }

  return NULL;
}

/* Adapted from gboxed.c */
static gchar*
proxy_lcopy_value (const GValue *value,
		   guint         n_collect_values,
		   GTypeCValue  *collect_values,
		   guint         collect_flags)
{
  gpointer *boxed_p = collect_values[0].v_pointer;

  if (!boxed_p)
    return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));

  if (!value->data[0].v_pointer)
    *boxed_p = NULL;
  else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
    *boxed_p = value->data[0].v_pointer;
  else
    {
      DBusGTypeSpecializedData *data;
      GType type;

      type = G_VALUE_TYPE (value);
      data = lookup_specialization_data (type);

      *boxed_p = data->klass->vtable->copy_func (type, value->data[0].v_pointer);
    }

  return NULL;
}

static char *
build_specialization_name (const char *prefix, GType first_type, GType second_type)
{
  GString *fullname;

  fullname = g_string_new (prefix);
  g_string_append_c (fullname, '+');
  g_string_append (fullname, g_type_name (first_type));
  if (second_type != G_TYPE_INVALID)
    {
      g_string_append_c (fullname, '+');
      g_string_append (fullname, g_type_name (second_type));
    }
  return g_string_free (fullname, FALSE);
}

static void
register_container (const char                         *name,
		    DBusGTypeSpecializedType            type,
		    const DBusGTypeSpecializedVtable   *vtable)
{
  DBusGTypeSpecializedContainer *klass;
  
  klass = g_new0 (DBusGTypeSpecializedContainer, 1);
  klass->type = type;
  klass->vtable = vtable;

  g_hash_table_insert (specialized_containers, g_strdup (name), klass);
}

void
dbus_g_type_register_collection (const char                                   *name,
				 const DBusGTypeSpecializedCollectionVtable   *vtable,
				 guint                                         flags)
{
  g_return_if_fail (specialized_types_is_initialized ());
  register_container (name, DBUS_G_SPECTYPE_COLLECTION, (const DBusGTypeSpecializedVtable*) vtable);
}

void
dbus_g_type_register_map (const char                            *name,
			  const DBusGTypeSpecializedMapVtable   *vtable,
			  guint                                  flags)
{
  g_return_if_fail (specialized_types_is_initialized ());
  register_container (name, DBUS_G_SPECTYPE_MAP, (const DBusGTypeSpecializedVtable*) vtable);
}

static GType
register_specialized_instance (const DBusGTypeSpecializedContainer   *klass,
			       char                                  *name,
			       GType                                  first_type,
			       GType                                  second_type)
{
  GType ret;
  
  static const GTypeValueTable vtable =
    {
      proxy_value_init,
      proxy_value_free,
      proxy_value_copy,
      proxy_value_peek_pointer,
      "p",
      proxy_collect_value,
      "p",
      proxy_lcopy_value,
    };
  static const GTypeInfo derived_info =
    {
      0,		/* class_size */
      NULL,		/* base_init */
      NULL,		/* base_finalize */
      NULL,		/* class_init */
      NULL,		/* class_finalize */
      NULL,		/* class_data */
      0,		/* instance_size */
      0,		/* n_preallocs */
      NULL,		/* instance_init */
      &vtable,		/* value_table */
    };

  ret = g_type_register_static (G_TYPE_BOXED, name, &derived_info, 0);
  /* install proxy functions upon successfull registration */
  if (ret != G_TYPE_INVALID)
    {
      DBusGTypeSpecializedData *data;
      data = g_new0 (DBusGTypeSpecializedData, 1);
      data->types[0] = first_type;
      data->types[1] = second_type;
      data->klass = klass;
      g_type_set_qdata (ret, specialized_type_data_quark (), data);
    }

  return ret;
}

static GType
lookup_or_register_specialized (const char  *container,
				GType       first_type,
				GType       second_type)
{
  GType ret;
  char *name;
  const DBusGTypeSpecializedContainer *klass;

  g_return_val_if_fail (specialized_types_is_initialized (), G_TYPE_INVALID);

  klass = g_hash_table_lookup (specialized_containers, container);
  g_return_val_if_fail (klass != NULL, G_TYPE_INVALID);

  name = build_specialization_name (container, first_type, second_type);
  ret = g_type_from_name (name);
  if (ret == G_TYPE_INVALID)
    {
      /* Take ownership of name */
      ret = register_specialized_instance (klass, name,
					   first_type,
					   second_type);
    }
  else
    g_free (name);
  return ret;
}

GType
dbus_g_type_get_collection (const char *container,
			    GType       specialization)
{
  return lookup_or_register_specialized (container, specialization, G_TYPE_INVALID);
}

GType
dbus_g_type_get_map (const char   *container,
		     GType         key_specialization,
		     GType         value_specialization)
{
  return lookup_or_register_specialized (container, key_specialization, value_specialization);
}

gboolean
dbus_g_type_is_collection (GType gtype)
{
  DBusGTypeSpecializedData *data;
  data = lookup_specialization_data (gtype);
  if (data == NULL)
    return FALSE;
  return data->klass->type == DBUS_G_SPECTYPE_COLLECTION;
}

gboolean
dbus_g_type_is_map (GType gtype)
{
  DBusGTypeSpecializedData *data;
  data = lookup_specialization_data (gtype);
  if (data == NULL)
    return FALSE;
  return data->klass->type == DBUS_G_SPECTYPE_MAP;
}

static GType
get_specialization_index (GType gtype, guint i)
{
  DBusGTypeSpecializedData *data;

  data = lookup_specialization_data (gtype);
  return data->types[i];
}

GType
dbus_g_type_get_collection_specialization (GType gtype)
{
  g_return_val_if_fail (dbus_g_type_is_collection (gtype), G_TYPE_INVALID);
  return get_specialization_index (gtype, 0);
}

GType
dbus_g_type_get_map_key_specialization (GType gtype)
{
  g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
  return get_specialization_index (gtype, 0);
}

GType
dbus_g_type_get_map_value_specialization (GType gtype)
{
  g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
  return get_specialization_index (gtype, 1);
}

gpointer
dbus_g_type_specialized_construct (GType type)
{
  DBusGTypeSpecializedData *data;
  g_return_val_if_fail (specialized_types_is_initialized (), FALSE);

  data = lookup_specialization_data (type);
  g_return_val_if_fail (data != NULL, FALSE);

  return data->klass->vtable->constructor (type);
}

gboolean
dbus_g_type_collection_get_fixed (GValue   *value,
				  gpointer *data_ret,
				  guint    *len_ret)
{
  DBusGTypeSpecializedData *data;
  GType gtype;

  g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
  g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);

  gtype = G_VALUE_TYPE (value);
  data = lookup_specialization_data (gtype);
  g_return_val_if_fail (data != NULL, FALSE);

  return ((DBusGTypeSpecializedCollectionVtable *) (data->klass->vtable))->fixed_accessor (gtype,
											   g_value_get_boxed (value),
											   data_ret, len_ret);
}

void
dbus_g_type_collection_value_iterate (GValue                                 *value,
				      DBusGTypeSpecializedCollectionIterator  iterator,
				      gpointer                                user_data)
{
  DBusGTypeSpecializedData *data;
  GType gtype;

  g_return_if_fail (specialized_types_is_initialized ());
  g_return_if_fail (G_VALUE_HOLDS_BOXED (value));

  gtype = G_VALUE_TYPE (value);
  data = lookup_specialization_data (gtype);
  g_return_if_fail (data != NULL);

  ((DBusGTypeSpecializedCollectionVtable *) data->klass->vtable)->iterator (gtype,
									    g_value_get_boxed (value),
									    iterator, user_data);
}

void
dbus_g_type_map_value_iterate (GValue                                 *value,
			       DBusGTypeSpecializedMapIterator         iterator,
			       gpointer                                user_data)
{
  DBusGTypeSpecializedData *data;
  GType gtype;

  g_return_if_fail (specialized_types_is_initialized ());
  g_return_if_fail (G_VALUE_HOLDS_BOXED (value));

  gtype = G_VALUE_TYPE (value);
  data = lookup_specialization_data (gtype);
  g_return_if_fail (data != NULL);

  ((DBusGTypeSpecializedMapVtable *) data->klass->vtable)->iterator (gtype,
								     g_value_get_boxed (value),
								     iterator, user_data);
}
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gvalue-utils.h: Non-DBus-specific functions related to GType/GValue
 *
 * Copyright (C) 2005 Red Hat, Inc.
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef DBUS_GOBJECT_VALUE_UTILS_H
#define DBUS_GOBJECT_VALUE_UTILS_H

#include <glib.h>
#include <glib-object.h>

G_BEGIN_DECLS

void           dbus_g_type_specialized_builtins_init (void);

gboolean       dbus_g_type_is_fixed                  (GType gtype); 
guint          dbus_g_type_fixed_get_size            (GType gtype); 

gboolean       dbus_gvalue_set_from_pointer          (GValue *value,
						      gconstpointer storage);

typedef void (*DBusGHashValueForeachFunc) (GValue * key, GValue *val, gpointer data);

void           dbus_g_hash_table_value_foreach       (GHashTable                *table,
						      GType                      hash_type,
						      DBusGHashValueForeachFunc  func,
						      gpointer                   data);

void           dbus_g_hash_table_insert_values       (GHashTable                *table,
						      GValue                    *key_val,
						      GValue                    *value_val);
void           dbus_g_hash_table_insert_steal_values (GHashTable *table,
						      GValue     *key_val,
						      GValue     *value_val);

gboolean       dbus_gtype_is_valid_hash_key          (GType type);
gboolean       dbus_gtype_is_valid_hash_value        (GType type);

GHashFunc      dbus_g_hash_func_from_gtype           (GType gtype);
GEqualFunc     dbus_g_hash_equal_from_gtype          (GType gtype);
GDestroyNotify dbus_g_hash_free_from_gtype           (GType gtype);

gboolean       dbus_gvalue_store                     (GValue          *value,
						      gpointer         storage);

gboolean       dbus_gvalue_take                      (GValue          *value,
						      GTypeCValue     *cvalue);


G_END_DECLS

#endif
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gvalue-utils.c: Non-DBus-specific functions related to GType/GValue 
 *
 * Copyright (C) 2005 Red Hat, Inc.
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "dbus/dbus-glib.h"
#include "dbus-gvalue-utils.h"
#include <glib.h>
#include <string.h>
#include <gobject/gvaluecollector.h>


static guint
fixed_type_get_size (GType type)
{
  switch (type)
    {
    case G_TYPE_CHAR:
    case G_TYPE_UCHAR:
      return sizeof (gchar);
    case G_TYPE_BOOLEAN:
      return sizeof (gboolean);
    case G_TYPE_LONG:
    case G_TYPE_ULONG:
      return sizeof (glong);
    case G_TYPE_INT:
    case G_TYPE_UINT:
      return sizeof (gint);
    case G_TYPE_INT64:
    case G_TYPE_UINT64:
      return sizeof (gint64);
    case G_TYPE_FLOAT:
      return sizeof (gfloat);
    case G_TYPE_DOUBLE:
      return sizeof (gdouble);
    default:
      return 0;
    }
}

gboolean
dbus_g_type_is_fixed (GType type)
{
  return fixed_type_get_size (type) > 0;
}

guint
dbus_g_type_fixed_get_size (GType type)
{
  g_assert (dbus_g_type_is_fixed (type));
  return fixed_type_get_size (type);
}

gboolean
dbus_gvalue_store (GValue          *value,
		   gpointer        storage)
{
  /* FIXME - can we use the GValue lcopy_value method
   * to do this in a cleaner way?
   */
  switch (g_type_fundamental (G_VALUE_TYPE (value)))
    {
    case G_TYPE_CHAR:
      *((gchar *) storage) = g_value_get_char (value);
      return TRUE;
    case G_TYPE_UCHAR:
      *((guchar *) storage) = g_value_get_uchar (value);
      return TRUE;
    case G_TYPE_BOOLEAN:
      *((gboolean *) storage) = g_value_get_boolean (value);
      return TRUE;
    case G_TYPE_LONG:
      *((glong *) storage) = g_value_get_long (value);
      return TRUE;
    case G_TYPE_ULONG:
      *((gulong *) storage) = g_value_get_ulong (value);
      return TRUE;
    case G_TYPE_INT:
      *((gint *) storage) = g_value_get_int (value);
      return TRUE;
    case G_TYPE_UINT:
      *((guint *) storage) = g_value_get_uint (value);
      return TRUE;
    case G_TYPE_INT64:
      *((gint64 *) storage) = g_value_get_int64 (value);
      return TRUE;
    case G_TYPE_UINT64:
      *((guint64 *) storage) = g_value_get_uint64 (value);
      return TRUE;
    case G_TYPE_DOUBLE:
      *((gdouble *) storage) = g_value_get_double (value);
      return TRUE;
    case G_TYPE_STRING:
      *((gchar **) storage) = (char*) g_value_get_string (value);
      return TRUE;
    case G_TYPE_POINTER:
      *((gpointer *) storage) = g_value_get_pointer (value);
      return TRUE;
    case G_TYPE_OBJECT:
      *((gpointer *) storage) = g_value_get_object (value);
      return TRUE;
    case G_TYPE_BOXED:
      *((gpointer *) storage) = g_value_get_boxed (value);
      return TRUE;
    default:
      return FALSE;
    }
}

gboolean
dbus_gvalue_set_from_pointer (GValue          *value,
			      gconstpointer    storage)
{
  /* FIXME - is there a better way to do this? */
  switch (g_type_fundamental (G_VALUE_TYPE (value)))
    {
    case G_TYPE_CHAR:
      g_value_set_char (value, *((gchar *) storage));
      return TRUE;
    case G_TYPE_UCHAR:
      g_value_set_uchar (value, *((guchar *) storage));
      return TRUE;
    case G_TYPE_BOOLEAN:
      g_value_set_boolean (value, *((gboolean *) storage));
      return TRUE;
    case G_TYPE_LONG:
      g_value_set_long (value, *((glong *) storage));
      return TRUE;
    case G_TYPE_ULONG:
      g_value_set_ulong (value, *((gulong *) storage));
      return TRUE;
    case G_TYPE_INT:
      g_value_set_int (value, *((gint *) storage));
      return TRUE;
    case G_TYPE_UINT:
      g_value_set_uint (value, *((guint *) storage));
      return TRUE;
    case G_TYPE_INT64:
      g_value_set_int64 (value, *((gint64 *) storage));
      return TRUE;
    case G_TYPE_UINT64:
      g_value_set_uint64 (value, *((guint64 *) storage));
      return TRUE;
    case G_TYPE_DOUBLE:
      g_value_set_double (value, *((gdouble *) storage));
      return TRUE;
    case G_TYPE_STRING:
      g_value_set_string (value, *((gchar **) storage));
      return TRUE;
    case G_TYPE_POINTER:
      g_value_set_pointer (value, *((gpointer *) storage));
      return TRUE;
    case G_TYPE_OBJECT:
      g_value_set_object (value, *((gpointer *) storage));
      return TRUE;
    case G_TYPE_BOXED:
      g_value_set_boxed (value, *((gpointer *) storage));
      return TRUE;
    default:
      return FALSE;
    }
}

gboolean
dbus_gvalue_take (GValue          *value,
		  GTypeCValue     *cvalue)
{
  GType g_type;
  GTypeValueTable *value_table;
  char *error_msg;

  g_type = G_VALUE_TYPE (value);
  value_table = g_type_value_table_peek (g_type);

  error_msg = value_table->collect_value (value, 1, cvalue, G_VALUE_NOCOPY_CONTENTS);
  if (error_msg)
    {
      g_warning ("%s: %s", G_STRLOC, error_msg);
      g_free (error_msg);
      return FALSE;
    }
  /* Clear the NOCOPY_CONTENTS flag; we want to take ownership
   * of the value.
   */
  value->data[1].v_uint &= ~(G_VALUE_NOCOPY_CONTENTS);
  return TRUE;
}

static gboolean
hash_func_from_gtype (GType gtype, GHashFunc *func)
{
  switch (gtype)
    {
    case G_TYPE_CHAR:
    case G_TYPE_UCHAR:
    case G_TYPE_BOOLEAN:
    case G_TYPE_INT:
    case G_TYPE_UINT:
      *func = NULL;
      return TRUE;
    case G_TYPE_STRING:
      *func = g_str_hash;
      return TRUE;
    default:
      return FALSE;
    }
}

static gboolean
hash_free_from_gtype (GType gtype, GDestroyNotify *func)
{
  switch (gtype)
    {
    case G_TYPE_CHAR:
    case G_TYPE_UCHAR:
    case G_TYPE_BOOLEAN:
    case G_TYPE_INT:
    case G_TYPE_UINT:
      *func = NULL;
      return TRUE;
    case G_TYPE_STRING:
      *func = g_free;
      return TRUE;
    default:
      if (gtype == G_TYPE_VALUE)
	{
	  *func = (GDestroyNotify) g_value_unset;
	  return TRUE;
	}
      return FALSE;
    }
}

gboolean
dbus_gtype_is_valid_hash_key (GType type)
{
  GHashFunc func;
  return hash_func_from_gtype (type, &func);
}

gboolean
dbus_gtype_is_valid_hash_value (GType type)
{
  GDestroyNotify func;
  return hash_free_from_gtype (type, &func);
}

GHashFunc
dbus_g_hash_func_from_gtype (GType gtype)
{
  GHashFunc func;
  gboolean ret;
  ret = hash_func_from_gtype (gtype, &func);
  g_assert (ret != FALSE);
  return func;
}

GEqualFunc
dbus_g_hash_equal_from_gtype (GType gtype)
{
  g_assert (dbus_gtype_is_valid_hash_key (gtype));

  switch (gtype)
    {
    case G_TYPE_CHAR:
    case G_TYPE_UCHAR:
    case G_TYPE_BOOLEAN:
    case G_TYPE_INT:
    case G_TYPE_UINT:
      return NULL;
    case G_TYPE_STRING:
      return g_str_equal;
    default:
      g_assert_not_reached ();
      return NULL;
    }
}

GDestroyNotify
dbus_g_hash_free_from_gtype (GType gtype)
{
  GDestroyNotify func;
  gboolean ret;
  ret = hash_free_from_gtype (gtype, &func);
  g_assert (ret != FALSE);
  return func;
}

static void
gvalue_from_hash_value (GValue *value, gpointer instance)
{
  switch (g_type_fundamental (G_VALUE_TYPE (value)))
    {
    case G_TYPE_CHAR:
      g_value_set_char (value, (gchar) GPOINTER_TO_INT (instance));
      break;
    case G_TYPE_UCHAR:
      g_value_set_uchar (value, (guchar) GPOINTER_TO_UINT (instance));
      break;
    case G_TYPE_BOOLEAN:
      g_value_set_boolean (value, (gboolean) GPOINTER_TO_UINT (instance));
      break;
    case G_TYPE_INT:
      g_value_set_int (value, GPOINTER_TO_INT (instance));
      break;
    case G_TYPE_UINT:
      g_value_set_uint (value, GPOINTER_TO_UINT (instance));
      break;
    case G_TYPE_STRING:
      g_value_set_static_string (value, instance);
      break;
    case G_TYPE_POINTER:
      g_value_set_pointer (value, instance);
      break;
    case G_TYPE_BOXED:
      g_value_set_static_boxed (value, instance);
      break;
    case G_TYPE_OBJECT:
      g_value_set_object (value, instance);
      g_object_unref (g_value_get_object (value));
      break;
    default:
      g_assert_not_reached ();
      break;
    }
}

static gpointer
hash_value_from_gvalue (GValue *value)
{
  switch (g_type_fundamental (G_VALUE_TYPE (value)))
    {
    case G_TYPE_CHAR:
      return GINT_TO_POINTER ((int) g_value_get_char (value));
      break;
    case G_TYPE_UCHAR:
      return GUINT_TO_POINTER ((guint) g_value_get_uchar (value));
      break;
    case G_TYPE_BOOLEAN:
      return GUINT_TO_POINTER ((guint) g_value_get_boolean (value));
      break;
    case G_TYPE_INT:
      return GINT_TO_POINTER (g_value_get_int (value));
      break;
    case G_TYPE_UINT:
      return GUINT_TO_POINTER (g_value_get_uint (value));
      break;
    case G_TYPE_STRING:
      return (gpointer) g_value_get_string (value);
      break;
    case G_TYPE_POINTER:
      return g_value_get_pointer (value);
      break;
    case G_TYPE_BOXED:
      return g_value_get_boxed (value);
      break;
    case G_TYPE_OBJECT:
      return g_value_get_object (value);
      break;
    default:
      g_assert_not_reached ();
      return NULL;
    }
}

struct DBusGHashTableValueForeachData
{
  DBusGTypeSpecializedMapIterator func;
  GType key_type;
  GType value_type;
  gpointer data;
};

static void
hashtable_foreach_with_values (gpointer key, gpointer value, gpointer user_data)
{
  GValue key_val = {0, };
  GValue value_val = {0, };
  struct DBusGHashTableValueForeachData *data = user_data;
  
  g_value_init (&key_val, data->key_type);
  g_value_init (&value_val, data->value_type);
  gvalue_from_hash_value (&key_val, key);
  gvalue_from_hash_value (&value_val, value);

  data->func (&key_val, &value_val, data->data);
}


static void
hashtable_iterator (GType                           hash_type,
		    gpointer                        instance,
		    DBusGTypeSpecializedMapIterator iterator,
		    gpointer                        user_data)
{
  struct DBusGHashTableValueForeachData data;
  GType key_gtype;
  GType value_gtype;

  key_gtype = dbus_g_type_get_map_key_specialization (hash_type);
  value_gtype = dbus_g_type_get_map_value_specialization (hash_type);

  data.func = iterator;
  data.key_type = key_gtype;
  data.value_type = value_gtype;
  data.data = user_data;

  g_hash_table_foreach (instance, hashtable_foreach_with_values, &data);
}

void
dbus_g_hash_table_insert_steal_values (GHashTable *table,
				       GValue     *key_val,
				       GValue     *value_val)
{
  gpointer key, val;
  
  key = hash_value_from_gvalue (key_val);
  val = hash_value_from_gvalue (value_val);

  g_hash_table_insert (table, key, val);
}

static gpointer
hashtable_constructor (GType type)
{
  GHashTable *ret;
  GType key_gtype;
  GType value_gtype;

  key_gtype = dbus_g_type_get_map_key_specialization (type);
  value_gtype = dbus_g_type_get_map_value_specialization (type);

  ret = g_hash_table_new_full (dbus_g_hash_func_from_gtype (key_gtype),
			       dbus_g_hash_equal_from_gtype (key_gtype),
			       dbus_g_hash_free_from_gtype (key_gtype),
			       dbus_g_hash_free_from_gtype (value_gtype));
  return ret;
}

static void
hashtable_insert_values (GHashTable       *table,
			 const GValue     *key_val,
			 const GValue     *value_val)
{
  GValue key_copy = {0, };
  GValue value_copy = {0, };

  g_value_init (&key_copy, G_VALUE_TYPE (key_val));
  g_value_copy (key_val, &key_copy);
  g_value_init (&value_copy, G_VALUE_TYPE (value_val));
  g_value_copy (value_val, &value_copy);
  
  dbus_g_hash_table_insert_steal_values (table, &key_copy, &value_copy);
}

static void
hashtable_foreach_copy (const GValue *key, const GValue *val, gpointer data)
{
  hashtable_insert_values ((GHashTable *) data, key, val);
}

static gpointer
hashtable_copy (GType type, gpointer src)
{
  GHashTable *ghash;
  GHashTable *ret;
  GValue hashval = {0,};

  ghash = src;

  ret = hashtable_constructor (type);

  g_value_init (&hashval, type);
  g_value_set_static_boxed (&hashval, ghash); 
  dbus_g_type_map_value_iterate (&hashval, hashtable_foreach_copy, ret);
  return ret;
}

static void
hashtable_free (GType type, gpointer val)
{
  g_hash_table_destroy (val);
}

static gpointer
array_constructor (GType type)
{
  GArray *array;
  guint elt_size;
  GType elt_type;
  gboolean zero_terminated;
  gboolean clear;

  elt_type = dbus_g_type_get_collection_specialization (type);
  g_assert (elt_type != G_TYPE_INVALID);

  elt_size = dbus_g_type_fixed_get_size (elt_type);

  /* These are "safe" defaults */ 
  zero_terminated = TRUE; /* ((struct _DBusGRealArray*) garray)->zero_terminated; */
  clear = TRUE; /* ((struct _DBusGRealArray*) garray)->clear; */

  array = g_array_new (zero_terminated, clear, elt_size);
  return array;
}

static gpointer
array_copy (GType type, gpointer src)
{
  GArray *garray;
  GArray *new;

  garray = src;

  new = array_constructor (type);
  g_array_append_vals (new, garray->data, garray->len);

  return new;
}

static void
array_free (GType type, gpointer val)
{
  GArray *array;
  array = val;
  g_array_free (array, TRUE);
}

static gboolean
array_fixed_accessor (GType type, gpointer instance, gpointer *values, guint *len)
{
  GType elt_type;
  GArray *array = instance;

  elt_type = dbus_g_type_get_collection_specialization (type);
  if (!dbus_g_type_is_fixed (elt_type))
    return FALSE;

  *values = array->data;
  *len = array->len;
  return TRUE;
}

static gpointer
ptrarray_constructor (GType type)
{
  /* Later we should determine a destructor, need g_ptr_array_destroy */
  return g_ptr_array_new ();
}

static void
gvalue_from_ptrarray_value (GValue *value, gpointer instance)
{
  switch (g_type_fundamental (G_VALUE_TYPE (value)))
    {
    case G_TYPE_POINTER:
      g_value_set_pointer (value, instance);
      break;
    case G_TYPE_BOXED:
      g_value_set_static_boxed (value, instance);
      break;
    case G_TYPE_OBJECT:
      g_value_set_object (value, instance);
      g_object_unref (g_value_get_object (value));
      break;
    default:
      g_assert_not_reached ();
      break;
    }
}

static gpointer
ptrarray_value_from_gvalue (const GValue *value)
{
  switch (g_type_fundamental (G_VALUE_TYPE (value)))
    {
    case G_TYPE_POINTER:
      return g_value_get_pointer (value);
      break;
    case G_TYPE_BOXED:
      return g_value_get_boxed (value);
      break;
    case G_TYPE_OBJECT:
      return g_value_get_object (value);
      break;
    default:
      g_assert_not_reached ();
      return NULL;
    }
}

static void
ptrarray_iterator (GType                                   hash_type,
		   gpointer                                instance,
		   DBusGTypeSpecializedCollectionIterator  iterator,
		   gpointer                                user_data)
{
  GPtrArray *ptrarray;
  GType elt_gtype;
  guint i;

  ptrarray = instance;

  elt_gtype = dbus_g_type_get_collection_specialization (hash_type);

  for (i = 0; i < ptrarray->len; i++)
    {
      GValue val = {0, };
      g_value_init (&val, elt_gtype);
      gvalue_from_ptrarray_value (&val, g_ptr_array_index (ptrarray, i));
      iterator (&val, user_data);
    }
}

static void
ptrarray_copy_elt (const GValue *val, gpointer user_data)
{
  GPtrArray *dest = user_data;
  GValue val_copy = {0, }; 
  
  g_value_init (&val_copy, G_VALUE_TYPE (val));
  g_value_copy (val, &val_copy);

  g_ptr_array_add (dest, ptrarray_value_from_gvalue (&val_copy));
}

static gpointer
ptrarray_copy (GType type, gpointer src)
{
  GPtrArray *new;
  GValue array_val = {0, };

  g_value_init (&array_val, type);
  g_value_set_static_boxed (&array_val, src);

  new = ptrarray_constructor (type);
  dbus_g_type_collection_value_iterate (&array_val, ptrarray_copy_elt, new);

  return new;
}

static void
ptrarray_free (GType type, gpointer val)
{
  GArray *array;
  array = val;
  g_array_free (array, TRUE);
}

void
dbus_g_type_specialized_builtins_init (void)
{
  static const DBusGTypeSpecializedCollectionVtable array_vtable = {
    {
      array_constructor,
      array_free,
      array_copy,
      NULL,
      NULL,
      NULL
    },
    array_fixed_accessor,
    NULL
  };

  dbus_g_type_register_collection ("GArray", &array_vtable, 0);

  static const DBusGTypeSpecializedCollectionVtable ptrarray_vtable = {
    {
      ptrarray_constructor,
      ptrarray_free,
      ptrarray_copy,
      NULL,
      NULL,
      NULL
    },
    NULL,
    ptrarray_iterator
  };

  dbus_g_type_register_collection ("GPtrArray", &ptrarray_vtable, 0);

  static const DBusGTypeSpecializedMapVtable hashtable_vtable = {
    {
      hashtable_constructor,
      hashtable_free,
      hashtable_copy,
      NULL,
      NULL,
      NULL
    },
    hashtable_iterator
  };

  dbus_g_type_register_map ("GHashTable", &hashtable_vtable, 0);
}

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]