avro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dcrea...@apache.org
Subject svn commit: r1175832 [2/5] - in /avro/trunk: ./ lang/c/docs/ lang/c/examples/ lang/c/src/ lang/c/src/avro/ lang/c/tests/
Date Mon, 26 Sep 2011 12:36:27 GMT
Modified: avro/trunk/lang/c/src/generic.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/generic.c?rev=1175832&r1=1175831&r2=1175832&view=diff
==============================================================================
--- avro/trunk/lang/c/src/generic.c (original)
+++ avro/trunk/lang/c/src/generic.c Mon Sep 26 12:36:26 2011
@@ -26,10 +26,102 @@
 #include "avro/refcount.h"
 #include "avro/schema.h"
 #include "avro/value.h"
+#include "avro_generic_internal.h"
 #include "avro_private.h"
 
+
+/*-----------------------------------------------------------------------
+ * Forward definitions
+ */
+
+typedef struct avro_generic_link_value_iface  avro_generic_link_value_iface_t;
+
+typedef struct memoize_state_t {
+	avro_memoize_t  mem;
+	avro_generic_link_value_iface_t  *links;
+} memoize_state_t;
+
+static avro_generic_value_iface_t *
+avro_generic_class_from_schema_memoized(avro_schema_t schema,
+					memoize_state_t *state);
+
+
+/*-----------------------------------------------------------------------
+ * Generic support functions
+ */
+
+int
+avro_generic_value_new(avro_value_iface_t *iface, avro_value_t *dest)
+{
+	int  rval;
+	avro_generic_value_iface_t  *giface =
+	    container_of(iface, avro_generic_value_iface_t, parent);
+	size_t  instance_size = avro_value_instance_size(giface);
+	void  *self = avro_malloc(instance_size + sizeof(volatile int));
+	if (self == NULL) {
+		avro_set_error(strerror(ENOMEM));
+		dest->iface = NULL;
+		dest->self = NULL;
+		return ENOMEM;
+	}
+
+	volatile int  *refcount = self;
+	self += sizeof(volatile int);
+
+	*refcount = 1;
+	rval = avro_value_init(giface, self);
+	if (rval != 0) {
+		avro_free(self, instance_size);
+		dest->iface = NULL;
+		dest->self = NULL;
+		return rval;
+	}
+
+	dest->iface = avro_value_iface_incref(&giface->parent);
+	dest->self = self;
+	return 0;
+}
+
+static void
+avro_generic_value_free(const avro_value_iface_t *iface, void *self)
+{
+	if (self != NULL) {
+		const avro_generic_value_iface_t  *giface =
+		    container_of(iface, avro_generic_value_iface_t, parent);
+		size_t  instance_size = avro_value_instance_size(giface);
+		avro_value_done(giface, self);
+		self -= sizeof(volatile int);
+		avro_free(self, instance_size + sizeof(volatile int));
+	}
+}
+
+static void
+avro_generic_value_incref(avro_value_t *value)
+{
+	/*
+	 * This only works if you pass in the top-level value.
+	 */
+
+	volatile int  *refcount = (value->self - sizeof(volatile int));
+	avro_refcount_inc(refcount);
+}
+
+static void
+avro_generic_value_decref(avro_value_t *value)
+{
+	/*
+	 * This only works if you pass in the top-level value.
+	 */
+
+	volatile int  *refcount = (value->self - sizeof(volatile int));
+	if (avro_refcount_dec(refcount)) {
+		avro_generic_value_free(value->iface, value->self);
+	}
+}
+
+
 /*-----------------------------------------------------------------------
- * recursive schemas
+ * Recursive schemas
  */
 
 /*
@@ -51,8 +143,8 @@
  * link schemas we encountered.
  */
 
-typedef struct avro_generic_link_value_iface {
-	avro_value_iface_t  iface;
+struct avro_generic_link_value_iface {
+	avro_generic_value_iface_t  parent;
 
 	/** The reference count for this interface. */
 	volatile int  refcount;
@@ -61,55 +153,41 @@ typedef struct avro_generic_link_value_i
 	avro_schema_t  schema;
 
 	/** The target's implementation. */
-	avro_value_iface_t  *target_iface;
-} avro_generic_link_value_iface_t;
+	avro_generic_value_iface_t  *target_giface;
+
+	/**
+	 * A pointer to the “next” link interface that we've had to
+	 * create.  We use this as we're creating the overall top-level
+	 * value interface to keep track of which ones we have to fix up
+	 * afterwards.
+	 */
+	avro_generic_link_value_iface_t  *next;
+};
 
 
 static avro_value_iface_t *
-avro_generic_link_incref(avro_value_iface_t *viface)
+avro_generic_link_incref_iface(avro_value_iface_t *viface)
 {
 	avro_generic_link_value_iface_t  *iface =
-	    (avro_generic_link_value_iface_t *) viface;
+	    container_of(viface, avro_generic_link_value_iface_t, parent);
 	avro_refcount_inc(&iface->refcount);
 	return viface;
 }
 
 static void
-avro_generic_link_decref(avro_value_iface_t *viface)
+avro_generic_link_decref_iface(avro_value_iface_t *viface)
 {
 	avro_generic_link_value_iface_t  *iface =
-	    (avro_generic_link_value_iface_t *) viface;
+	    container_of(viface, avro_generic_link_value_iface_t, parent.parent);
 
 	if (avro_refcount_dec(&iface->refcount)) {
-		avro_value_iface_decref(iface->target_iface);
+		/* We don't keep a reference to the target
+		 * implementation, since that would give us a reference
+		 * cycle. */
 		avro_freet(avro_generic_link_value_iface_t, iface);
 	}
 }
 
-static size_t
-avro_generic_link_instance_size(const avro_value_iface_t *viface)
-{
-	AVRO_UNUSED(viface);
-	return sizeof(avro_value_t);
-}
-
-static int
-avro_generic_link_init(const avro_value_iface_t *viface, void *vself)
-{
-	avro_generic_link_value_iface_t  *iface =
-	    (avro_generic_link_value_iface_t *) viface;
-	avro_value_t  *self = vself;
-
-	return avro_value_new(iface->target_iface, self);
-}
-
-static void
-avro_generic_link_done(const avro_value_iface_t *iface, void *vself)
-{
-	AVRO_UNUSED(iface);
-	avro_value_t  *self = vself;
-	avro_value_free(self);
-}
 
 static int
 avro_generic_link_reset(const avro_value_iface_t *iface, void *vself)
@@ -453,60 +531,113 @@ avro_generic_link_set_branch(const avro_
 	return avro_value_set_branch(self, discriminant, branch);
 }
 
-static avro_value_iface_t  AVRO_GENERIC_LINK_CLASS =
+static size_t
+avro_generic_link_instance_size(const avro_value_iface_t *viface)
 {
-	/* "class" methods */
-	avro_generic_link_incref,
-	avro_generic_link_decref,
+	AVRO_UNUSED(viface);
+	return sizeof(avro_value_t);
+}
+
+static int
+avro_generic_link_init(const avro_value_iface_t *viface, void *vself)
+{
+	int  rval;
+
+	avro_generic_link_value_iface_t  *iface =
+	    container_of(viface, avro_generic_link_value_iface_t, parent.parent);
+
+	avro_value_t  *self = vself;
+	size_t  target_instance_size =
+	    avro_value_instance_size(iface->target_giface);
+	if (target_instance_size == 0) {
+		return EINVAL;
+	}
+
+	self->iface = &iface->target_giface->parent;
+	self->self = avro_malloc(target_instance_size);
+	if (self->self == NULL) {
+		return ENOMEM;
+	}
+
+	rval = avro_value_init(iface->target_giface, self->self);
+	if (rval != 0) {
+		avro_free(self->self, target_instance_size);
+	}
+	return rval;
+}
+
+static void
+avro_generic_link_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_value_t  *self = vself;
+	avro_generic_value_iface_t  *target_giface =
+	    container_of(self->iface, avro_generic_value_iface_t, parent);
+	size_t  target_instance_size = avro_value_instance_size(target_giface);
+	avro_value_done(target_giface, self->self);
+	avro_free(self->self, target_instance_size);
+	self->iface = NULL;
+	self->self = NULL;
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_LINK_CLASS =
+{
+	{
+		/* "class" methods */
+		avro_generic_link_incref_iface,
+		avro_generic_link_decref_iface,
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_link_reset,
+		avro_generic_link_get_type,
+		avro_generic_link_get_schema,
+		/* primitive getters */
+		avro_generic_link_get_boolean,
+		avro_generic_link_get_bytes,
+		avro_generic_link_grab_bytes,
+		avro_generic_link_get_double,
+		avro_generic_link_get_float,
+		avro_generic_link_get_int,
+		avro_generic_link_get_long,
+		avro_generic_link_get_null,
+		avro_generic_link_get_string,
+		avro_generic_link_grab_string,
+		avro_generic_link_get_enum,
+		avro_generic_link_get_fixed,
+		avro_generic_link_grab_fixed,
+		/* primitive setters */
+		avro_generic_link_set_boolean,
+		avro_generic_link_set_bytes,
+		avro_generic_link_give_bytes,
+		avro_generic_link_set_double,
+		avro_generic_link_set_float,
+		avro_generic_link_set_int,
+		avro_generic_link_set_long,
+		avro_generic_link_set_null,
+		avro_generic_link_set_string,
+		avro_generic_link_set_string_len,
+		avro_generic_link_give_string_len,
+		avro_generic_link_set_enum,
+		avro_generic_link_set_fixed,
+		avro_generic_link_give_fixed,
+		/* compound getters */
+		avro_generic_link_get_size,
+		avro_generic_link_get_by_index,
+		avro_generic_link_get_by_name,
+		avro_generic_link_get_discriminant,
+		avro_generic_link_get_current_branch,
+		/* compound setters */
+		avro_generic_link_append,
+		avro_generic_link_add,
+		avro_generic_link_set_branch
+	},
 	avro_generic_link_instance_size,
-	/* general "instance" methods */
 	avro_generic_link_init,
-	avro_generic_link_done,
-	avro_generic_link_reset,
-	avro_generic_link_get_type,
-	avro_generic_link_get_schema,
-	/* primitive getters */
-	avro_generic_link_get_boolean,
-	avro_generic_link_get_bytes,
-	avro_generic_link_grab_bytes,
-	avro_generic_link_get_double,
-	avro_generic_link_get_float,
-	avro_generic_link_get_int,
-	avro_generic_link_get_long,
-	avro_generic_link_get_null,
-	avro_generic_link_get_string,
-	avro_generic_link_grab_string,
-	avro_generic_link_get_enum,
-	avro_generic_link_get_fixed,
-	avro_generic_link_grab_fixed,
-	/* primitive setters */
-	avro_generic_link_set_boolean,
-	avro_generic_link_set_bytes,
-	avro_generic_link_give_bytes,
-	avro_generic_link_set_double,
-	avro_generic_link_set_float,
-	avro_generic_link_set_int,
-	avro_generic_link_set_long,
-	avro_generic_link_set_null,
-	avro_generic_link_set_string,
-	avro_generic_link_set_string_len,
-	avro_generic_link_give_string_len,
-	avro_generic_link_set_enum,
-	avro_generic_link_set_fixed,
-	avro_generic_link_give_fixed,
-	/* compound getters */
-	avro_generic_link_get_size,
-	avro_generic_link_get_by_index,
-	avro_generic_link_get_by_name,
-	avro_generic_link_get_discriminant,
-	avro_generic_link_get_current_branch,
-	/* compound setters */
-	avro_generic_link_append,
-	avro_generic_link_add,
-	avro_generic_link_set_branch
+	avro_generic_link_done
 };
 
-static avro_value_iface_t *
+static avro_generic_link_value_iface_t *
 avro_generic_link_class(avro_schema_t schema)
 {
 	if (!is_avro_link(schema)) {
@@ -520,164 +651,3039 @@ avro_generic_link_class(avro_schema_t sc
 		return NULL;
 	}
 
-	memcpy(&iface->iface, &AVRO_GENERIC_LINK_CLASS,
-	       sizeof(avro_value_iface_t));
+	iface->parent = AVRO_GENERIC_LINK_CLASS;
 	iface->refcount = 1;
 	iface->schema = avro_schema_incref(schema);
-	return &iface->iface;
+	iface->next = NULL;
+	return iface;
 }
 
 
 /*-----------------------------------------------------------------------
- * schema type dispatcher
+ * boolean
  */
 
-typedef struct memoize_state_t {
-	avro_memoize_t  mem;
-	avro_raw_array_t  links;
-} memoize_state_t;
-
-static avro_value_iface_t *
-avro_generic_class_from_schema_memoized(avro_schema_t schema,
-					void *user_data)
+static int
+avro_generic_boolean_reset(const avro_value_iface_t *iface, void *vself)
 {
-	memoize_state_t  *state = user_data;
-
-	/*
-	 * If we've already instantiated a value class for this schema,
-	 * just return it.
-	 */
-
-	avro_value_iface_t  *result = NULL;
-	if (avro_memoize_get(&state->mem, schema, NULL, (void **) &result)) {
-		return avro_value_iface_incref(result);
-	}
+	AVRO_UNUSED(iface);
+	int  *self = vself;
+	*self = 0;
+	return 0;
+}
 
-	/*
-	 * Otherwise instantiate the value class based on the schema
-	 * type.
-	 */
+static avro_type_t
+avro_generic_boolean_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_BOOLEAN;
+}
 
-	switch (schema->type) {
-	case AVRO_BOOLEAN:
-		result = avro_generic_boolean_class();
-		break;
-	case AVRO_BYTES:
-		result = avro_generic_bytes_class();
-		break;
-	case AVRO_DOUBLE:
-		result = avro_generic_double_class();
-		break;
-	case AVRO_FLOAT:
-		result = avro_generic_float_class();
-		break;
-	case AVRO_INT32:
-		result = avro_generic_int_class();
-		break;
-	case AVRO_INT64:
-		result = avro_generic_long_class();
-		break;
-	case AVRO_NULL:
-		result = avro_generic_null_class();
-		break;
-	case AVRO_STRING:
-		result = avro_generic_string_class();
-		break;
+static avro_schema_t
+avro_generic_boolean_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_boolean();
+}
 
-	case AVRO_ARRAY:
-		result = avro_generic_array_class
-		    (schema, avro_generic_class_from_schema_memoized, state);
-		break;
-	case AVRO_ENUM:
-		result = avro_generic_enum_class(schema);
-		break;
-	case AVRO_FIXED:
-		result = avro_generic_fixed_class(schema);
-		break;
-	case AVRO_MAP:
-		result = avro_generic_map_class
-		    (schema, avro_generic_class_from_schema_memoized, state);
-		break;
-	case AVRO_RECORD:
-		result = avro_generic_record_class
-		    (schema, avro_generic_class_from_schema_memoized, state);
-		break;
-	case AVRO_UNION:
-		result = avro_generic_union_class
-		    (schema, avro_generic_class_from_schema_memoized, state);
-		break;
+static int
+avro_generic_boolean_get(const avro_value_iface_t *iface,
+			 const void *vself, int *out)
+{
+	AVRO_UNUSED(iface);
+	const int  *self = vself;
+	*out = *self;
+	return 0;
+}
 
-	case AVRO_LINK:
-		{
-			avro_value_iface_t  **link_ptr =
-			    avro_raw_array_append(&state->links);
-			result = avro_generic_link_class(schema);
-			*link_ptr = result;
-			break;
-		}
+static int
+avro_generic_boolean_set(const avro_value_iface_t *iface,
+			 void *vself, int val)
+{
+	AVRO_UNUSED(iface);
+	int  *self = vself;
+	*self = val;
+	return 0;
+}
 
-	default:
-		avro_set_error("Unknown schema type");
-		return NULL;
-	}
+static size_t
+avro_generic_boolean_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(int);
+}
 
-	/*
-	 * Add the new value implementation to the memoized state before
-	 * we return.
-	 */
+static int
+avro_generic_boolean_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int  *self = vself;
+	*self = 0;
+	return 0;
+}
 
-	avro_memoize_set(&state->mem, schema, NULL, result);
-	return result;
+static void
+avro_generic_boolean_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
 }
 
-avro_value_iface_t *
-avro_generic_class_from_schema(avro_schema_t schema)
+static avro_generic_value_iface_t  AVRO_GENERIC_BOOLEAN_CLASS =
 {
-	/*
-	 * Create a state to keep track of the value implementations
-	 * that we create for each subschema.
-	 */
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_boolean_reset,
+		avro_generic_boolean_get_type,
+		avro_generic_boolean_get_schema,
+		/* primitive getters */
+		avro_generic_boolean_get,
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		avro_generic_boolean_set,
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_boolean_instance_size,
+	avro_generic_boolean_init,
+	avro_generic_boolean_done
+};
 
-	memoize_state_t  state;
-	avro_memoize_init(&state.mem);
-	avro_raw_array_init(&state.links, sizeof(avro_value_iface_t *));
+avro_value_iface_t *
+avro_generic_boolean_class(void)
+{
+	return &AVRO_GENERIC_BOOLEAN_CLASS.parent;
+}
 
-	/*
-	 * Create the value implementations.
-	 */
+int
+avro_generic_boolean_new(avro_value_t *value, int val)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_BOOLEAN_CLASS.parent, value));
+	return avro_generic_boolean_set(value->iface, value->self, val);
+}
 
-	avro_value_iface_t  *result =
-	    avro_generic_class_from_schema_memoized(schema, &state);
+/*-----------------------------------------------------------------------
+ * bytes
+ */
 
-	/*
-	 * Fix up any link schemas so that their value implementations
-	 * point to their target schemas' implementations.
-	 */
+static int
+avro_generic_bytes_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_clear(self);
+	return 0;
+}
 
-	size_t  i;
-	for (i = 0; i < avro_raw_array_size(&state.links); i++) {
-		avro_generic_link_value_iface_t  *link_iface =
-		    avro_raw_array_get
-		    (&state.links, avro_generic_link_value_iface_t *, i);
-		avro_schema_t  target_schema =
-		    avro_schema_link_target(link_iface->schema);
+static avro_type_t
+avro_generic_bytes_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_BYTES;
+}
 
-		avro_value_iface_t  *target_iface = NULL;
-		if (!avro_memoize_get(&state.mem, target_schema, NULL,
-				      (void **) &target_iface)) {
-			avro_set_error("Never created a value implementation for %s",
-				       avro_schema_type_name(target_schema));
-			return NULL;
-		}
+static avro_schema_t
+avro_generic_bytes_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_bytes();
+}
 
-		link_iface->target_iface = avro_value_iface_incref(target_iface);
+static int
+avro_generic_bytes_get(const avro_value_iface_t *iface,
+		       const void *vself, const void **buf, size_t *size)
+{
+	AVRO_UNUSED(iface);
+	const avro_raw_string_t  *self = vself;
+	if (buf != NULL) {
+		*buf = avro_raw_string_get(self);
+	}
+	if (size != NULL) {
+		*size = avro_raw_string_length(self);
 	}
+	return 0;
+}
 
-	/*
-	 * And now we can return.
-	 */
+static int
+avro_generic_bytes_grab(const avro_value_iface_t *iface,
+			const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(iface);
+	const avro_raw_string_t  *self = vself;
+	return avro_raw_string_grab(self, dest);
+}
 
-	avro_memoize_done(&state.mem);
-	avro_raw_array_done(&state.links);
-	return result;
+static int
+avro_generic_bytes_set(const avro_value_iface_t *iface,
+		       void *vself, void *buf, size_t size)
+{
+	AVRO_UNUSED(iface);
+	check_param(EINVAL, buf != NULL, "bytes contents");
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_set_length(self, buf, size);
+	return 0;
+}
+
+static int
+avro_generic_bytes_give(const avro_value_iface_t *iface,
+			void *vself, avro_wrapped_buffer_t *buf)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_give(self, buf);
+	return 0;
+}
+
+static size_t
+avro_generic_bytes_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(avro_raw_string_t);
+}
+
+static int
+avro_generic_bytes_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_init(self);
+	return 0;
+}
+
+static void
+avro_generic_bytes_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_done(self);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_BYTES_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_bytes_reset,
+		avro_generic_bytes_get_type,
+		avro_generic_bytes_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		avro_generic_bytes_get,
+		avro_generic_bytes_grab,
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		avro_generic_bytes_set,
+		avro_generic_bytes_give,
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_bytes_instance_size,
+	avro_generic_bytes_init,
+	avro_generic_bytes_done
+};
+
+avro_value_iface_t *
+avro_generic_bytes_class(void)
+{
+	return &AVRO_GENERIC_BYTES_CLASS.parent;
+}
+
+int
+avro_generic_bytes_new(avro_value_t *value, void *buf, size_t size)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_BYTES_CLASS.parent, value));
+	return avro_generic_bytes_set(value->iface, value->self, buf, size);
+}
+
+/*-----------------------------------------------------------------------
+ * double
+ */
+
+static int
+avro_generic_double_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	double  *self = vself;
+	*self = 0.0;
+	return 0;
+}
+
+static avro_type_t
+avro_generic_double_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_DOUBLE;
+}
+
+static avro_schema_t
+avro_generic_double_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_double();
+}
+
+static int
+avro_generic_double_get(const avro_value_iface_t *iface,
+			const void *vself, double *out)
+{
+	AVRO_UNUSED(iface);
+	const double  *self = vself;
+	*out = *self;
+	return 0;
+}
+
+static int
+avro_generic_double_set(const avro_value_iface_t *iface,
+			void *vself, double val)
+{
+	AVRO_UNUSED(iface);
+	double  *self = vself;
+	*self = val;
+	return 0;
+}
+
+static size_t
+avro_generic_double_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(double);
+}
+
+static int
+avro_generic_double_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	double  *self = vself;
+	*self = 0.0;
+	return 0;
+}
+
+static void
+avro_generic_double_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_DOUBLE_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_double_reset,
+		avro_generic_double_get_type,
+		avro_generic_double_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		avro_generic_double_get,
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		avro_generic_double_set,
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_double_instance_size,
+	avro_generic_double_init,
+	avro_generic_double_done
+};
+
+avro_value_iface_t *
+avro_generic_double_class(void)
+{
+	return &AVRO_GENERIC_DOUBLE_CLASS.parent;
+}
+
+int
+avro_generic_double_new(avro_value_t *value, double val)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_DOUBLE_CLASS.parent, value));
+	return avro_generic_double_set(value->iface, value->self, val);
+}
+
+/*-----------------------------------------------------------------------
+ * float
+ */
+
+static int
+avro_generic_float_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	float  *self = vself;
+	*self = 0.0f;
+	return 0;
+}
+
+static avro_type_t
+avro_generic_float_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_FLOAT;
+}
+
+static avro_schema_t
+avro_generic_float_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_float();
+}
+
+static int
+avro_generic_float_get(const avro_value_iface_t *iface,
+		       const void *vself, float *out)
+{
+	AVRO_UNUSED(iface);
+	const float  *self = vself;
+	*out = *self;
+	return 0;
+}
+
+static int
+avro_generic_float_set(const avro_value_iface_t *iface,
+		       void *vself, float val)
+{
+	AVRO_UNUSED(iface);
+	float  *self = vself;
+	*self = val;
+	return 0;
+}
+
+static size_t
+avro_generic_float_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(float);
+}
+
+static int
+avro_generic_float_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	float  *self = vself;
+	*self = 0.0f;
+	return 0;
+}
+
+static void
+avro_generic_float_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_FLOAT_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_float_reset,
+		avro_generic_float_get_type,
+		avro_generic_float_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		avro_generic_float_get,
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		avro_generic_float_set,
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_float_instance_size,
+	avro_generic_float_init,
+	avro_generic_float_done
+};
+
+avro_value_iface_t *
+avro_generic_float_class(void)
+{
+	return &AVRO_GENERIC_FLOAT_CLASS.parent;
+}
+
+int
+avro_generic_float_new(avro_value_t *value, float val)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_FLOAT_CLASS.parent, value));
+	return avro_generic_float_set(value->iface, value->self, val);
+}
+
+/*-----------------------------------------------------------------------
+ * int
+ */
+
+static int
+avro_generic_int_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int32_t  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static avro_type_t
+avro_generic_int_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_INT32;
+}
+
+static avro_schema_t
+avro_generic_int_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_int();
+}
+
+static int
+avro_generic_int_get(const avro_value_iface_t *iface,
+		     const void *vself, int32_t *out)
+{
+	AVRO_UNUSED(iface);
+	const int32_t  *self = vself;
+	*out = *self;
+	return 0;
+}
+
+static int
+avro_generic_int_set(const avro_value_iface_t *iface,
+		     void *vself, int32_t val)
+{
+	AVRO_UNUSED(iface);
+	int32_t  *self = vself;
+	*self = val;
+	return 0;
+}
+
+static size_t
+avro_generic_int_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(int32_t);
+}
+
+static int
+avro_generic_int_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int32_t  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static void
+avro_generic_int_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_INT_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_int_reset,
+		avro_generic_int_get_type,
+		avro_generic_int_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		avro_generic_int_get,
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		avro_generic_int_set,
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_int_instance_size,
+	avro_generic_int_init,
+	avro_generic_int_done
+};
+
+avro_value_iface_t *
+avro_generic_int_class(void)
+{
+	return &AVRO_GENERIC_INT_CLASS.parent;
+}
+
+int
+avro_generic_int_new(avro_value_t *value, int32_t val)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_INT_CLASS.parent, value));
+	return avro_generic_int_set(value->iface, value->self, val);
+}
+
+/*-----------------------------------------------------------------------
+ * long
+ */
+
+static int
+avro_generic_long_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int64_t  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static avro_type_t
+avro_generic_long_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_INT64;
+}
+
+static avro_schema_t
+avro_generic_long_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_long();
+}
+
+static int
+avro_generic_long_get(const avro_value_iface_t *iface,
+		      const void *vself, int64_t *out)
+{
+	AVRO_UNUSED(iface);
+	const int64_t  *self = vself;
+	*out = *self;
+	return 0;
+}
+
+static int
+avro_generic_long_set(const avro_value_iface_t *iface,
+		      void *vself, int64_t val)
+{
+	AVRO_UNUSED(iface);
+	int64_t  *self = vself;
+	*self = val;
+	return 0;
+}
+
+static size_t
+avro_generic_long_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(int64_t);
+}
+
+static int
+avro_generic_long_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int64_t  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static void
+avro_generic_long_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_LONG_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_long_reset,
+		avro_generic_long_get_type,
+		avro_generic_long_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		avro_generic_long_get,
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		avro_generic_long_set,
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_long_instance_size,
+	avro_generic_long_init,
+	avro_generic_long_done
+};
+
+avro_value_iface_t *
+avro_generic_long_class(void)
+{
+	return &AVRO_GENERIC_LONG_CLASS.parent;
+}
+
+int
+avro_generic_long_new(avro_value_t *value, int64_t val)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_LONG_CLASS.parent, value));
+	return avro_generic_long_set(value->iface, value->self, val);
+}
+
+/*-----------------------------------------------------------------------
+ * null
+ */
+
+static int
+avro_generic_null_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static avro_type_t
+avro_generic_null_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_NULL;
+}
+
+static avro_schema_t
+avro_generic_null_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_null();
+}
+
+static int
+avro_generic_null_get(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return 0;
+}
+
+static int
+avro_generic_null_set(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return 0;
+}
+
+static size_t
+avro_generic_null_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(int);
+}
+
+static int
+avro_generic_null_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	int  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static void
+avro_generic_null_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_NULL_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_null_reset,
+		avro_generic_null_get_type,
+		avro_generic_null_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		avro_generic_null_get,
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		avro_generic_null_set,
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_null_instance_size,
+	avro_generic_null_init,
+	avro_generic_null_done
+};
+
+avro_value_iface_t *
+avro_generic_null_class(void)
+{
+	return &AVRO_GENERIC_NULL_CLASS.parent;
+}
+
+int
+avro_generic_null_new(avro_value_t *value)
+{
+	return avro_generic_value_new(&AVRO_GENERIC_NULL_CLASS.parent, value);
+}
+
+/*-----------------------------------------------------------------------
+ * string
+ */
+
+static int
+avro_generic_string_reset(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_clear(self);
+	return 0;
+}
+
+static avro_type_t
+avro_generic_string_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_STRING;
+}
+
+static avro_schema_t
+avro_generic_string_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return avro_schema_string();
+}
+
+static int
+avro_generic_string_get(const avro_value_iface_t *iface,
+			const void *vself, const char **str, size_t *size)
+{
+	AVRO_UNUSED(iface);
+	const avro_raw_string_t  *self = vself;
+	const char  *contents = avro_raw_string_get(self);
+
+	if (str != NULL) {
+		/*
+		 * We can't return a NULL string, we have to return an
+		 * *empty* string
+		 */
+
+		*str = (contents == NULL)? "": contents;
+	}
+	if (size != NULL) {
+		/* raw_string's length includes the NUL terminator,
+		 * unless it's empty */
+		*size = (contents == NULL)? 1: avro_raw_string_length(self);
+	}
+	return 0;
+}
+
+static int
+avro_generic_string_grab(const avro_value_iface_t *iface,
+			 const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(iface);
+	const avro_raw_string_t  *self = vself;
+	const char  *contents = avro_raw_string_get(self);
+
+	if (contents == NULL) {
+		return avro_wrapped_buffer_new(dest, "", 1);
+	} else {
+		return avro_raw_string_grab(self, dest);
+	}
+}
+
+static int
+avro_generic_string_set(const avro_value_iface_t *iface,
+			void *vself, char *val)
+{
+	AVRO_UNUSED(iface);
+	check_param(EINVAL, val != NULL, "string contents");
+
+	/*
+	 * This raw_string method ensures that we copy the NUL
+	 * terminator from val, and will include the NUL terminator in
+	 * the raw_string's length, which is what we want.
+	 */
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_set(self, val);
+	return 0;
+}
+
+static int
+avro_generic_string_set_length(const avro_value_iface_t *iface,
+			       void *vself, char *val, size_t size)
+{
+	AVRO_UNUSED(iface);
+	check_param(EINVAL, val != NULL, "string contents");
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_set_length(self, val, size);
+	return 0;
+}
+
+static int
+avro_generic_string_give_length(const avro_value_iface_t *iface,
+				void *vself, avro_wrapped_buffer_t *buf)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_give(self, buf);
+	return 0;
+}
+
+static size_t
+avro_generic_string_instance_size(const avro_value_iface_t *iface)
+{
+	AVRO_UNUSED(iface);
+	return sizeof(avro_raw_string_t);
+}
+
+static int
+avro_generic_string_init(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_init(self);
+	return 0;
+}
+
+static void
+avro_generic_string_done(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_raw_string_t  *self = vself;
+	avro_raw_string_done(self);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_STRING_CLASS =
+{
+	{
+		/* "class" methods */
+		NULL, /* incref_iface */
+		NULL, /* decref_iface */
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_string_reset,
+		avro_generic_string_get_type,
+		avro_generic_string_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		avro_generic_string_get,
+		avro_generic_string_grab,
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		avro_generic_string_set,
+		avro_generic_string_set_length,
+		avro_generic_string_give_length,
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_string_instance_size,
+	avro_generic_string_init,
+	avro_generic_string_done
+};
+
+avro_value_iface_t *
+avro_generic_string_class(void)
+{
+	return &AVRO_GENERIC_STRING_CLASS.parent;
+}
+
+int
+avro_generic_string_new(avro_value_t *value, char *str)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_STRING_CLASS.parent, value));
+	return avro_generic_string_set(value->iface, value->self, str);
+}
+
+int
+avro_generic_string_new_length(avro_value_t *value, char *str, size_t size)
+{
+	int  rval;
+	check(rval, avro_generic_value_new(&AVRO_GENERIC_STRING_CLASS.parent, value));
+	return avro_generic_string_set_length(value->iface, value->self, str, size);
+}
+
+
+/*-----------------------------------------------------------------------
+ * array
+ */
+
+/*
+ * For generic arrays, we need to store the value implementation for the
+ * array's elements.
+ */
+
+typedef struct avro_generic_array_value_iface {
+	avro_generic_value_iface_t  parent;
+	volatile int  refcount;
+	avro_schema_t  schema;
+	avro_generic_value_iface_t  *child_giface;
+} avro_generic_array_value_iface_t;
+
+typedef struct avro_generic_array {
+	avro_raw_array_t  array;
+} avro_generic_array_t;
+
+
+static avro_value_iface_t *
+avro_generic_array_incref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+avro_generic_array_decref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	if (avro_refcount_dec(&iface->refcount)) {
+		avro_schema_decref(iface->schema);
+		avro_value_iface_decref(&iface->child_giface->parent);
+		avro_freet(avro_generic_array_value_iface_t, iface);
+	}
+}
+
+
+static void
+avro_generic_array_free_elements(const avro_generic_value_iface_t *child_giface,
+				 avro_generic_array_t *self)
+{
+	size_t  i;
+	for (i = 0; i < avro_raw_array_size(&self->array); i++) {
+		void  *child_self = avro_raw_array_get_raw(&self->array, i);
+		avro_value_done(child_giface, child_self);
+	}
+}
+
+static int
+avro_generic_array_reset(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	avro_generic_array_t  *self = vself;
+	avro_generic_array_free_elements(iface->child_giface, self);
+	avro_raw_array_clear(&self->array);
+	return 0;
+}
+
+static avro_type_t
+avro_generic_array_get_type(const avro_value_iface_t *viface, const void *vself)
+{
+	AVRO_UNUSED(viface);
+	AVRO_UNUSED(vself);
+	return AVRO_ARRAY;
+}
+
+static avro_schema_t
+avro_generic_array_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	const avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	return iface->schema;
+}
+
+static int
+avro_generic_array_get_size(const avro_value_iface_t *viface,
+			    const void *vself, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_generic_array_t  *self = vself;
+	if (size != NULL) {
+		*size = avro_raw_array_size(&self->array);
+	}
+	return 0;
+}
+
+static int
+avro_generic_array_get_by_index(const avro_value_iface_t *viface,
+				const void *vself, size_t index,
+				avro_value_t *child, const char **name)
+{
+	const avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	AVRO_UNUSED(name);
+	const avro_generic_array_t  *self = vself;
+	if (index >= avro_raw_array_size(&self->array)) {
+		avro_set_error("Array index %zu out of range", index);
+		return EINVAL;
+	}
+	child->iface = &iface->child_giface->parent;
+	child->self = avro_raw_array_get_raw(&self->array, index);
+	return 0;
+}
+
+static int
+avro_generic_array_append(const avro_value_iface_t *viface,
+			  void *vself, avro_value_t *child,
+			  size_t *new_index)
+{
+	int  rval;
+	const avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	avro_generic_array_t  *self = vself;
+	child->iface = &iface->child_giface->parent;
+	child->self = avro_raw_array_append(&self->array);
+	if (child->self == NULL) {
+		avro_set_error("Couldn't expand array");
+		return ENOMEM;
+	}
+	check(rval, avro_value_init(iface->child_giface, child->self));
+	if (new_index != NULL) {
+		*new_index = avro_raw_array_size(&self->array) - 1;
+	}
+	return 0;
+}
+
+static size_t
+avro_generic_array_instance_size(const avro_value_iface_t *viface)
+{
+	AVRO_UNUSED(viface);
+	return sizeof(avro_generic_array_t);
+}
+
+static int
+avro_generic_array_init(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	avro_generic_array_t  *self = vself;
+
+	size_t  child_size = avro_value_instance_size(iface->child_giface);
+	avro_raw_array_init(&self->array, child_size);
+	return 0;
+}
+
+static void
+avro_generic_array_done(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_array_value_iface_t  *iface =
+	    container_of(viface, avro_generic_array_value_iface_t, parent);
+	avro_generic_array_t  *self = vself;
+	avro_generic_array_free_elements(iface->child_giface, self);
+	avro_raw_array_done(&self->array);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_ARRAY_CLASS =
+{
+{
+	/* "class" methods */
+	avro_generic_array_incref_iface,
+	avro_generic_array_decref_iface,
+	/* general "instance" methods */
+	avro_generic_value_incref,
+	avro_generic_value_decref,
+	avro_generic_array_reset,
+	avro_generic_array_get_type,
+	avro_generic_array_get_schema,
+	/* primitive getters */
+	NULL, /* get_boolean */
+	NULL, /* get_bytes */
+	NULL, /* grab_bytes */
+	NULL, /* get_double */
+	NULL, /* get_float */
+	NULL, /* get_int */
+	NULL, /* get_long */
+	NULL, /* get_null */
+	NULL, /* get_string */
+	NULL, /* grab_string */
+	NULL, /* get_enum */
+	NULL, /* get_fixed */
+	NULL, /* grab_fixed */
+	/* primitive setters */
+	NULL, /* set_boolean */
+	NULL, /* set_bytes */
+	NULL, /* give_bytes */
+	NULL, /* set_double */
+	NULL, /* set_float */
+	NULL, /* set_int */
+	NULL, /* set_long */
+	NULL, /* set_null */
+	NULL, /* set_string */
+	NULL, /* set_string_length */
+	NULL, /* give_string_length */
+	NULL, /* set_enum */
+	NULL, /* set_fixed */
+	NULL, /* give_fixed */
+	/* compound getters */
+	avro_generic_array_get_size,
+	avro_generic_array_get_by_index,
+	NULL, /* get_by_name */
+	NULL, /* get_discriminant */
+	NULL, /* get_current_branch */
+	/* compound setters */
+	avro_generic_array_append,
+	NULL, /* add */
+	NULL  /* set_branch */
+},
+	avro_generic_array_instance_size,
+	avro_generic_array_init,
+	avro_generic_array_done
+};
+
+static avro_generic_value_iface_t *
+avro_generic_array_class(avro_schema_t schema, memoize_state_t *state)
+{
+	avro_schema_t  child_schema = avro_schema_array_items(schema);
+	avro_generic_value_iface_t  *child_giface =
+	    avro_generic_class_from_schema_memoized(child_schema, state);
+	if (child_giface == NULL) {
+		return NULL;
+	}
+
+	size_t  child_size = avro_value_instance_size(child_giface);
+	if (child_size == 0) {
+		avro_set_error("Array item class must provide instance_size");
+		avro_value_iface_decref(&child_giface->parent);
+		return NULL;
+	}
+
+	avro_generic_array_value_iface_t  *iface =
+		avro_new(avro_generic_array_value_iface_t);
+	if (iface == NULL) {
+		avro_value_iface_decref(&child_giface->parent);
+		return NULL;
+	}
+
+	/*
+	 * TODO: Maybe check that schema.items matches
+	 * child_iface.get_schema?
+	 */
+
+	iface->parent = AVRO_GENERIC_ARRAY_CLASS;
+	iface->refcount = 1;
+	iface->schema = avro_schema_incref(schema);
+	iface->child_giface = child_giface;
+	return &iface->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * enum
+ */
+
+typedef struct avro_generic_enum_value_iface {
+	avro_generic_value_iface_t  parent;
+	volatile int  refcount;
+	avro_schema_t  schema;
+} avro_generic_enum_value_iface_t;
+
+
+static avro_value_iface_t *
+avro_generic_enum_incref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_enum_value_iface_t  *iface =
+	    (avro_generic_enum_value_iface_t *) viface;
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+avro_generic_enum_decref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_enum_value_iface_t  *iface =
+	    (avro_generic_enum_value_iface_t *) viface;
+	if (avro_refcount_dec(&iface->refcount)) {
+		avro_schema_decref(iface->schema);
+		avro_freet(avro_generic_enum_value_iface_t, iface);
+	}
+}
+
+static int
+avro_generic_enum_reset(const avro_value_iface_t *viface, void *vself)
+{
+	AVRO_UNUSED(viface);
+	int  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static avro_type_t
+avro_generic_enum_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_ENUM;
+}
+
+static avro_schema_t
+avro_generic_enum_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	const avro_generic_enum_value_iface_t  *iface =
+	    container_of(viface, avro_generic_enum_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	return iface->schema;
+}
+
+static int
+avro_generic_enum_get(const avro_value_iface_t *viface,
+		      const void *vself, int *out)
+{
+	AVRO_UNUSED(viface);
+	const int  *self = vself;
+	*out = *self;
+	return 0;
+}
+
+static int
+avro_generic_enum_set(const avro_value_iface_t *viface,
+		      void *vself, int val)
+{
+	AVRO_UNUSED(viface);
+	int  *self = vself;
+	*self = val;
+	return 0;
+}
+
+static size_t
+avro_generic_enum_instance_size(const avro_value_iface_t *viface)
+{
+	AVRO_UNUSED(viface);
+	return sizeof(int);
+}
+
+static int
+avro_generic_enum_init(const avro_value_iface_t *viface, void *vself)
+{
+	AVRO_UNUSED(viface);
+	int  *self = vself;
+	*self = 0;
+	return 0;
+}
+
+static void
+avro_generic_enum_done(const avro_value_iface_t *viface, void *vself)
+{
+	AVRO_UNUSED(viface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_ENUM_CLASS =
+{
+	{
+		/* "class" methods */
+		avro_generic_enum_incref_iface,
+		avro_generic_enum_decref_iface,
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_enum_reset,
+		avro_generic_enum_get_type,
+		avro_generic_enum_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		avro_generic_enum_get,
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		avro_generic_enum_set,
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_enum_instance_size,
+	avro_generic_enum_init,
+	avro_generic_enum_done
+};
+
+static avro_generic_value_iface_t *
+avro_generic_enum_class(avro_schema_t schema)
+{
+	avro_generic_enum_value_iface_t  *iface =
+		avro_new(avro_generic_enum_value_iface_t);
+	if (iface == NULL) {
+		return NULL;
+	}
+
+	iface->parent = AVRO_GENERIC_ENUM_CLASS;
+	iface->refcount = 1;
+	iface->schema = avro_schema_incref(schema);
+	return &iface->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * fixed
+ */
+
+typedef struct avro_generic_fixed_value_iface {
+	avro_generic_value_iface_t  parent;
+	volatile int  refcount;
+	avro_schema_t  schema;
+	size_t  data_size;
+} avro_generic_fixed_value_iface_t;
+
+
+static avro_value_iface_t *
+avro_generic_fixed_incref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+avro_generic_fixed_decref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	if (avro_refcount_dec(&iface->refcount)) {
+		avro_schema_decref(iface->schema);
+		avro_freet(avro_generic_fixed_value_iface_t, iface);
+	}
+}
+
+static int
+avro_generic_fixed_reset(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	memset(vself, 0, iface->data_size);
+	return 0;
+}
+
+static avro_type_t
+avro_generic_fixed_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	AVRO_UNUSED(vself);
+	return AVRO_FIXED;
+}
+
+static avro_schema_t
+avro_generic_fixed_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	return iface->schema;
+}
+
+static int
+avro_generic_fixed_get(const avro_value_iface_t *viface,
+		       const void *vself, const void **buf, size_t *size)
+{
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	if (buf != NULL) {
+		*buf = vself;
+	}
+	if (size != NULL) {
+		*size = iface->data_size;
+	}
+	return 0;
+}
+
+static int
+avro_generic_fixed_grab(const avro_value_iface_t *viface,
+			const void *vself, avro_wrapped_buffer_t *dest)
+{
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	return avro_wrapped_buffer_new(dest, vself, iface->data_size);
+}
+
+static int
+avro_generic_fixed_set(const avro_value_iface_t *viface,
+		       void *vself, void *buf, size_t size)
+{
+	check_param(EINVAL, buf != NULL, "fixed contents");
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	if (size != iface->data_size) {
+		avro_set_error("Invalid data size in set_fixed");
+		return EINVAL;
+	}
+	memcpy(vself, buf, size);
+	return 0;
+}
+
+static int
+avro_generic_fixed_give(const avro_value_iface_t *viface,
+			void *vself, avro_wrapped_buffer_t *buf)
+{
+	int  rval = avro_generic_fixed_set
+	    (viface, vself, (void *) buf->buf, buf->size);
+	avro_wrapped_buffer_free(buf);
+	return rval;
+}
+
+static size_t
+avro_generic_fixed_instance_size(const avro_value_iface_t *viface)
+{
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	return iface->data_size;
+}
+
+static int
+avro_generic_fixed_init(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_fixed_value_iface_t  *iface =
+	    container_of(viface, avro_generic_fixed_value_iface_t, parent);
+	memset(vself, 0, iface->data_size);
+	return 0;
+}
+
+static void
+avro_generic_fixed_done(const avro_value_iface_t *viface, void *vself)
+{
+	AVRO_UNUSED(viface);
+	AVRO_UNUSED(vself);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_FIXED_CLASS =
+{
+	{
+		/* "class" methods */
+		avro_generic_fixed_incref_iface,
+		avro_generic_fixed_decref_iface,
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_fixed_reset,
+		avro_generic_fixed_get_type,
+		avro_generic_fixed_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		avro_generic_fixed_get,
+		avro_generic_fixed_grab,
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		avro_generic_fixed_set,
+		avro_generic_fixed_give,
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_fixed_instance_size,
+	avro_generic_fixed_init,
+	avro_generic_fixed_done
+};
+
+static avro_generic_value_iface_t *
+avro_generic_fixed_class(avro_schema_t schema)
+{
+	avro_generic_fixed_value_iface_t  *iface =
+		avro_new(avro_generic_fixed_value_iface_t);
+	if (iface == NULL) {
+		return NULL;
+	}
+
+	iface->parent = AVRO_GENERIC_FIXED_CLASS;
+	iface->refcount = 1;
+	iface->schema = avro_schema_incref(schema);
+	iface->data_size = avro_schema_fixed_size(schema);
+	return &iface->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * map
+ */
+
+/*
+ * For generic maps, we need to store the value implementation for the
+ * map's elements.
+ */
+
+typedef struct avro_generic_map_value_iface {
+	avro_generic_value_iface_t  parent;
+	volatile int  refcount;
+	avro_schema_t  schema;
+	avro_generic_value_iface_t  *child_giface;
+} avro_generic_map_value_iface_t;
+
+typedef struct avro_generic_map {
+	avro_raw_map_t  map;
+} avro_generic_map_t;
+
+
+static avro_value_iface_t *
+avro_generic_map_incref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+avro_generic_map_decref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	if (avro_refcount_dec(&iface->refcount)) {
+		avro_schema_decref(iface->schema);
+		avro_value_iface_decref(&iface->child_giface->parent);
+		avro_freet(avro_generic_map_value_iface_t, iface);
+	}
+}
+
+
+static void
+avro_generic_map_free_elements(const avro_generic_value_iface_t *child_giface,
+			       avro_generic_map_t *self)
+{
+	size_t  i;
+	for (i = 0; i < avro_raw_map_size(&self->map); i++) {
+		void  *child_self = avro_raw_map_get_raw(&self->map, i);
+		avro_value_done(child_giface, child_self);
+	}
+}
+
+static int
+avro_generic_map_reset(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	avro_generic_map_t  *self = vself;
+	avro_generic_map_free_elements(iface->child_giface, self);
+	avro_raw_map_clear(&self->map);
+	return 0;
+}
+
+static avro_type_t
+avro_generic_map_get_type(const avro_value_iface_t *viface, const void *vself)
+{
+	AVRO_UNUSED(viface);
+	AVRO_UNUSED(vself);
+	return AVRO_MAP;
+}
+
+static avro_schema_t
+avro_generic_map_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	return iface->schema;
+}
+
+static int
+avro_generic_map_get_size(const avro_value_iface_t *viface,
+			  const void *vself, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_generic_map_t  *self = vself;
+	if (size != NULL) {
+		*size = avro_raw_map_size(&self->map);
+	}
+	return 0;
+}
+
+static int
+avro_generic_map_get_by_index(const avro_value_iface_t *viface,
+			      const void *vself, size_t index,
+			      avro_value_t *child, const char **name)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	const avro_generic_map_t  *self = vself;
+	if (index >= avro_raw_map_size(&self->map)) {
+		avro_set_error("Map index %zu out of range", index);
+		return EINVAL;
+	}
+	child->iface = &iface->child_giface->parent;
+	child->self = avro_raw_map_get_raw(&self->map, index);
+	if (name != NULL) {
+		*name = avro_raw_map_get_key(&self->map, index);
+	}
+	return 0;
+}
+
+static int
+avro_generic_map_get_by_name(const avro_value_iface_t *viface,
+			     const void *vself, const char *name,
+			     avro_value_t *child, size_t *index)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	const avro_generic_map_t  *self = vself;
+	child->iface = &iface->child_giface->parent;
+	child->self = avro_raw_map_get(&self->map, name, index);
+	return 0;
+}
+
+static int
+avro_generic_map_add(const avro_value_iface_t *viface,
+		     void *vself, const char *key,
+		     avro_value_t *child, size_t *index, int *is_new)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	int  rval;
+	avro_generic_map_t  *self = vself;
+	child->iface = &iface->child_giface->parent;
+	rval = avro_raw_map_get_or_create(&self->map, key,
+					  &child->self, index);
+	if (rval < 0) {
+		return -rval;
+	}
+	if (is_new != NULL) {
+		*is_new = rval;
+	}
+	if (rval) {
+		check(rval, avro_value_init(iface->child_giface, child->self));
+	}
+	return 0;
+}
+
+static size_t
+avro_generic_map_instance_size(const avro_value_iface_t *viface)
+{
+	AVRO_UNUSED(viface);
+	return sizeof(avro_generic_map_t);
+}
+
+static int
+avro_generic_map_init(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	avro_generic_map_t  *self = vself;
+
+	size_t  child_size = avro_value_instance_size(iface->child_giface);
+	avro_raw_map_init(&self->map, child_size);
+	return 0;
+}
+
+static void
+avro_generic_map_done(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_map_value_iface_t  *iface =
+	    container_of(viface, avro_generic_map_value_iface_t, parent);
+	avro_generic_map_t  *self = vself;
+	avro_generic_map_free_elements(iface->child_giface, self);
+	avro_raw_map_done(&self->map);
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_MAP_CLASS =
+{
+	{
+		/* "class" methods */
+		avro_generic_map_incref_iface,
+		avro_generic_map_decref_iface,
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_map_reset,
+		avro_generic_map_get_type,
+		avro_generic_map_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		avro_generic_map_get_size,
+		avro_generic_map_get_by_index,
+		avro_generic_map_get_by_name,
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		avro_generic_map_add,
+		NULL  /* set_branch */
+	},
+	avro_generic_map_instance_size,
+	avro_generic_map_init,
+	avro_generic_map_done
+};
+
+static avro_generic_value_iface_t *
+avro_generic_map_class(avro_schema_t schema, memoize_state_t *state)
+{
+	avro_schema_t  child_schema = avro_schema_array_items(schema);
+	avro_generic_value_iface_t  *child_giface =
+	    avro_generic_class_from_schema_memoized(child_schema, state);
+	if (child_giface == NULL) {
+		return NULL;
+	}
+
+	size_t  child_size = avro_value_instance_size(child_giface);
+	if (child_size == 0) {
+		avro_set_error("Map value class must provide instance_size");
+		avro_value_iface_decref(&child_giface->parent);
+		return NULL;
+	}
+
+	avro_generic_map_value_iface_t  *iface =
+		avro_new(avro_generic_map_value_iface_t);
+	if (iface == NULL) {
+		avro_value_iface_decref(&child_giface->parent);
+		return NULL;
+	}
+
+	/*
+	 * TODO: Maybe check that schema.items matches
+	 * child_iface.get_schema?
+	 */
+
+	iface->parent = AVRO_GENERIC_MAP_CLASS;
+	iface->refcount = 1;
+	iface->schema = avro_schema_incref(schema);
+	iface->child_giface = child_giface;
+	return &iface->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * record
+ */
+
+#ifndef DEBUG_FIELD_OFFSETS
+#define DEBUG_FIELD_OFFSETS 0
+#endif
+
+#if DEBUG_FIELD_OFFSETS
+#include <stdio.h>
+#endif
+
+/*
+ * For generic records, we need to store the value implementation for
+ * each field.  We also need to store an offset for each field, since
+ * we're going to store the contents of each field directly in the
+ * record, rather than via pointers.
+ */
+
+typedef struct avro_generic_record_value_iface {
+	avro_generic_value_iface_t  parent;
+	volatile int  refcount;
+	avro_schema_t  schema;
+
+	/** The total size of each value struct for this record. */
+	size_t  instance_size;
+
+	/** The number of fields in this record.  Yes, we could get this
+	 * from schema, but this is easier. */
+	size_t  field_count;
+
+	/** The offset of each field within the record struct. */
+	size_t  *field_offsets;
+
+	/** The value implementation for each field. */
+	avro_generic_value_iface_t  **field_ifaces;
+} avro_generic_record_value_iface_t;
+
+typedef struct avro_generic_record {
+	/* The rest of the struct is taken up by the inline storage
+	 * needed for each field. */
+} avro_generic_record_t;
+
+
+/** Return a pointer to the given field within a record struct. */
+#define avro_generic_record_field(iface, rec, index) \
+	(((void *) (rec)) + (iface)->field_offsets[(index)])
+
+
+static avro_value_iface_t *
+avro_generic_record_incref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+avro_generic_record_decref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+
+	if (avro_refcount_dec(&iface->refcount)) {
+		size_t  i;
+		for (i = 0; i < iface->field_count; i++) {
+			avro_value_iface_decref(&iface->field_ifaces[i]->parent);
+		}
+
+		avro_schema_decref(iface->schema);
+		avro_free(iface->field_offsets,
+			  sizeof(size_t) * iface->field_count);
+		avro_free(iface->field_ifaces,
+			  sizeof(avro_generic_value_iface_t *) * iface->field_count);
+
+		avro_freet(avro_generic_record_value_iface_t, iface);
+	}
+}
+
+
+static int
+avro_generic_record_reset(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	int  rval;
+	avro_generic_record_t  *self = vself;
+	size_t  i;
+	for (i = 0; i < iface->field_count; i++) {
+		avro_value_t  value = {
+			&iface->field_ifaces[i]->parent,
+			avro_generic_record_field(iface, self, i)
+		};
+		check(rval, avro_value_reset(&value));
+	}
+	return 0;
+}
+
+static avro_type_t
+avro_generic_record_get_type(const avro_value_iface_t *viface, const void *vself)
+{
+	AVRO_UNUSED(viface);
+	AVRO_UNUSED(vself);
+	return AVRO_RECORD;
+}
+
+static avro_schema_t
+avro_generic_record_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	return iface->schema;
+}
+
+static int
+avro_generic_record_get_size(const avro_value_iface_t *viface,
+			     const void *vself, size_t *size)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	if (size != NULL) {
+		*size = iface->field_count;
+	}
+	return 0;
+}
+
+static int
+avro_generic_record_get_by_index(const avro_value_iface_t *viface,
+				 const void *vself, size_t index,
+				 avro_value_t *child, const char **name)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	const avro_generic_record_t  *self = vself;
+	if (index >= iface->field_count) {
+		avro_set_error("Field index %zu out of range", index);
+		return EINVAL;
+	}
+	child->iface = &iface->field_ifaces[index]->parent;
+	child->self = avro_generic_record_field(iface, self, index);
+
+	/*
+	 * Grab the field name from the schema if asked for.
+	 */
+	if (name != NULL) {
+		avro_schema_t  schema = iface->schema;
+		*name = avro_schema_record_field_name(schema, index);
+	}
+
+	return 0;
+}
+
+static int
+avro_generic_record_get_by_name(const avro_value_iface_t *viface,
+				const void *vself, const char *name,
+				avro_value_t *child, size_t *index_out)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	const avro_generic_record_t  *self = vself;
+
+	avro_schema_t  schema = iface->schema;
+	int  index = avro_schema_record_field_get_index(schema, name);
+	if (index < 0) {
+		avro_set_error("Unknown record field %s", name);
+		return EINVAL;
+	}
+
+	child->iface = &iface->field_ifaces[index]->parent;
+	child->self = avro_generic_record_field(iface, self, index);
+	if (index_out != NULL) {
+		*index_out = index;
+	}
+	return 0;
+}
+
+static size_t
+avro_generic_record_instance_size(const avro_value_iface_t *viface)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	return iface->instance_size;
+}
+
+static int
+avro_generic_record_init(const avro_value_iface_t *viface, void *vself)
+{
+	int  rval;
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	avro_generic_record_t  *self = vself;
+
+	/* Initialize each field */
+	size_t  i;
+	for (i = 0; i < iface->field_count; i++) {
+		check(rval, avro_value_init
+		      (iface->field_ifaces[i],
+		       avro_generic_record_field(iface, self, i)));
+	}
+
+	return 0;
+}
+
+static void
+avro_generic_record_done(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_record_value_iface_t  *iface =
+	    container_of(viface, avro_generic_record_value_iface_t, parent);
+	avro_generic_record_t  *self = vself;
+	size_t  i;
+	for (i = 0; i < iface->field_count; i++) {
+		avro_value_done(iface->field_ifaces[i],
+				avro_generic_record_field(iface, self, i));
+	}
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_RECORD_CLASS =
+{
+	{
+		/* "class" methods */
+		avro_generic_record_incref_iface,
+		avro_generic_record_decref_iface,
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_record_reset,
+		avro_generic_record_get_type,
+		avro_generic_record_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		avro_generic_record_get_size,
+		avro_generic_record_get_by_index,
+		avro_generic_record_get_by_name,
+		NULL, /* get_discriminant */
+		NULL, /* get_current_branch */
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		NULL  /* set_branch */
+	},
+	avro_generic_record_instance_size,
+	avro_generic_record_init,
+	avro_generic_record_done
+};
+
+static avro_generic_value_iface_t *
+avro_generic_record_class(avro_schema_t schema, memoize_state_t *state)
+{
+	avro_generic_record_value_iface_t  *iface =
+		avro_new(avro_generic_record_value_iface_t);
+	if (iface == NULL) {
+		return NULL;
+	}
+
+	memset(iface, 0, sizeof(avro_generic_record_value_iface_t));
+	iface->parent = AVRO_GENERIC_RECORD_CLASS;
+	iface->refcount = 1;
+	iface->schema = avro_schema_incref(schema);
+
+	iface->field_count = avro_schema_record_size(schema);
+	size_t  field_offsets_size =
+		sizeof(size_t) * iface->field_count;
+	size_t  field_ifaces_size =
+		sizeof(avro_generic_value_iface_t *) * iface->field_count;
+
+	iface->field_offsets = avro_malloc(field_offsets_size);
+	if (iface->field_offsets == NULL) {
+		goto error;
+	}
+
+	iface->field_ifaces = avro_malloc(field_ifaces_size);
+	if (iface->field_ifaces == NULL) {
+		goto error;
+	}
+
+	size_t  next_offset = sizeof(avro_generic_record_t);
+#if DEBUG_FIELD_OFFSETS
+	fprintf(stderr, "  Record %s\n  Header: Offset 0, size %zu\n",
+		avro_schema_type_name(schema),
+		sizeof(avro_generic_record_t));
+#endif
+	size_t  i;
+	for (i = 0; i < iface->field_count; i++) {
+#if DEBUG_FIELD_OFFSETS
+		fprintf(stderr, "  Field %zu:\n", i);
+#endif
+		avro_schema_t  field_schema =
+		    avro_schema_record_field_get_by_index(schema, i);
+#if DEBUG_FIELD_OFFSETS
+		fprintf(stderr, "    Schema %s\n",
+			avro_schema_type_name(field_schema));
+#endif
+
+		iface->field_offsets[i] = next_offset;
+
+		iface->field_ifaces[i] =
+		    avro_generic_class_from_schema_memoized(field_schema, state);
+		if (iface->field_ifaces[i] == NULL) {
+			goto error;
+		}
+
+		size_t  field_size =
+		    avro_value_instance_size(iface->field_ifaces[i]);
+		if (field_size == 0) {
+			avro_set_error("Record field class must provide instance_size");
+			goto error;
+		}
+
+#if DEBUG_FIELD_OFFSETS
+		fprintf(stderr, "    Offset %zu, size %zu\n",
+			next_offset, field_size);
+#endif
+		next_offset += field_size;
+	}
+
+	iface->instance_size = next_offset;
+#if DEBUG_FIELD_OFFSETS
+	fprintf(stderr, "  TOTAL SIZE: %zu\n", next_offset);
+#endif
+
+	return &iface->parent;
+
+error:
+	avro_schema_decref(iface->schema);
+	if (iface->field_offsets != NULL) {
+		avro_free(iface->field_offsets, field_offsets_size);
+	}
+	if (iface->field_ifaces != NULL) {
+		for (i = 0; i < iface->field_count; i++) {
+			if (iface->field_ifaces[i] != NULL) {
+				avro_value_iface_decref(&iface->field_ifaces[i]->parent);
+			}
+		}
+		avro_free(iface->field_ifaces, field_ifaces_size);
+	}
+	avro_freet(avro_generic_record_value_iface_t, iface);
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * union
+ */
+
+#ifndef DEBUG_BRANCHES_OFFSETS
+#define DEBUG_BRANCHES_OFFSETS 0
+#endif
+
+#if DEBUG_BRANCHES_OFFSETS
+#include <stdio.h>
+#endif
+
+/*
+ * For generic unions, we need to store the value implementation for
+ * each branch, just like for generic records.  However, for unions, we
+ * can only have one branch active at a time, so we can reuse the space
+ * in the union struct, just like is done with C unions.
+ */
+
+typedef struct avro_generic_union_value_iface {
+	avro_generic_value_iface_t  parent;
+	volatile int  refcount;
+	avro_schema_t  schema;
+
+	/** The total size of each value struct for this union. */
+	size_t  instance_size;
+
+	/** The number of branches in this union.  Yes, we could get
+	 * this from schema, but this is easier. */
+	size_t  branch_count;
+
+	/** The value implementation for each branch. */
+	avro_generic_value_iface_t  **branch_ifaces;
+} avro_generic_union_value_iface_t;
+
+typedef struct avro_generic_union {
+	/** The currently active branch of the union.  -1 if no branch
+	 * is selected. */
+	int  discriminant;
+
+	/* The rest of the struct is taken up by the inline storage
+	 * needed for the active branch. */
+} avro_generic_union_t;
+
+
+/** Return the child interface for the active branch. */
+#define avro_generic_union_branch_giface(iface, _union) \
+	((iface)->branch_ifaces[(_union)->discriminant])
+#define avro_generic_union_branch_iface(iface, _union) \
+	(&(avro_generic_union_branch_giface((iface), (_union)))->parent)
+
+/** Return a pointer to the active branch within a union struct. */
+#define avro_generic_union_branch(_union) \
+	(((void *) (_union)) + sizeof(avro_generic_union_t))
+
+
+static avro_value_iface_t *
+avro_generic_union_incref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+avro_generic_union_decref_iface(avro_value_iface_t *viface)
+{
+	avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+
+	if (avro_refcount_dec(&iface->refcount)) {
+		size_t  i;
+		for (i = 0; i < iface->branch_count; i++) {
+			avro_value_iface_decref(&iface->branch_ifaces[i]->parent);
+		}
+
+		avro_schema_decref(iface->schema);
+		avro_free(iface->branch_ifaces,
+			  sizeof(avro_generic_value_iface_t *) * iface->branch_count);
+
+		avro_freet(avro_generic_union_value_iface_t, iface);
+	}
+}
+
+
+static int
+avro_generic_union_reset(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	avro_generic_union_t  *self = vself;
+	/* Keep the same branch selected, for the common case that we're
+	 * about to reuse it. */
+	if (self->discriminant >= 0) {
+#if DEBUG_BRANCHES
+		fprintf(stderr, "Resetting branch %d\n",
+			self->discriminant);
+#endif
+		avro_value_t  value = {
+			avro_generic_union_branch_iface(iface, self),
+			avro_generic_union_branch(self)
+		};
+		return avro_value_reset(&value);
+	}
+	return 0;
+}
+
+static avro_type_t
+avro_generic_union_get_type(const avro_value_iface_t *viface, const void *vself)
+{
+	AVRO_UNUSED(viface);
+	AVRO_UNUSED(vself);
+	return AVRO_UNION;
+}
+
+static avro_schema_t
+avro_generic_union_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	const avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	AVRO_UNUSED(vself);
+	return iface->schema;
+}
+
+static int
+avro_generic_union_get_discriminant(const avro_value_iface_t *viface,
+				    const void *vself, int *out)
+{
+	AVRO_UNUSED(viface);
+	const avro_generic_union_t  *self = vself;
+	*out = self->discriminant;
+	return 0;
+}
+
+static int
+avro_generic_union_get_current_branch(const avro_value_iface_t *viface,
+				      const void *vself, avro_value_t *branch)
+{
+	const avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	const avro_generic_union_t  *self = vself;
+	if (self->discriminant < 0) {
+		avro_set_error("Union has no selected branch");
+		return EINVAL;
+	}
+	branch->iface = avro_generic_union_branch_iface(iface, self);
+	branch->self = avro_generic_union_branch(self);
+	return 0;
+}
+
+static int
+avro_generic_union_set_branch(const avro_value_iface_t *viface,
+			      void *vself, int discriminant,
+			      avro_value_t *branch)
+{
+	const avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	int  rval;
+	avro_generic_union_t  *self = vself;
+
+#if DEBUG_BRANCHES
+	fprintf(stderr, "Selecting branch %d (was %d)\n",
+		discriminant, self->discriminant);
+#endif
+
+	/*
+	 * If the new desired branch is different than the currently
+	 * active one, then finalize the old branch and initialize the
+	 * new one.
+	 */
+	if (self->discriminant != discriminant) {
+		if (self->discriminant >= 0) {
+#if DEBUG_BRANCHES
+			fprintf(stderr, "Finalizing branch %d\n",
+				self->discriminant);
+#endif
+			avro_value_done
+			    (avro_generic_union_branch_giface(iface, self),
+			     avro_generic_union_branch(self));
+		}
+		self->discriminant = discriminant;
+		if (discriminant >= 0) {
+#if DEBUG_BRANCHES
+			fprintf(stderr, "Initializing branch %d\n",
+				self->discriminant);
+#endif
+			check(rval, avro_value_init
+			      (avro_generic_union_branch_giface(iface, self),
+			       avro_generic_union_branch(self)));
+		}
+	}
+
+	if (branch != NULL) {
+		branch->iface = avro_generic_union_branch_iface(iface, self);
+		branch->self = avro_generic_union_branch(self);
+	}
+
+	return 0;
+}
+
+static size_t
+avro_generic_union_instance_size(const avro_value_iface_t *viface)
+{
+	const avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	return iface->instance_size;
+}
+
+static int
+avro_generic_union_init(const avro_value_iface_t *viface, void *vself)
+{
+	AVRO_UNUSED(viface);
+	avro_generic_union_t  *self = vself;
+	self->discriminant = -1;
+	return 0;
+}
+
+static void
+avro_generic_union_done(const avro_value_iface_t *viface, void *vself)
+{
+	const avro_generic_union_value_iface_t  *iface =
+	    container_of(viface, avro_generic_union_value_iface_t, parent);
+	avro_generic_union_t  *self = vself;
+	if (self->discriminant >= 0) {
+#if DEBUG_BRANCHES
+		fprintf(stderr, "Finalizing branch %d\n",
+			self->discriminant);
+#endif
+		avro_value_done
+		    (avro_generic_union_branch_giface(iface, self),
+		     avro_generic_union_branch(self));
+		self->discriminant = -1;
+	}
+}
+
+static avro_generic_value_iface_t  AVRO_GENERIC_UNION_CLASS =
+{
+	{
+		/* "class" methods */
+		avro_generic_union_incref_iface,
+		avro_generic_union_decref_iface,
+		/* general "instance" methods */
+		avro_generic_value_incref,
+		avro_generic_value_decref,
+		avro_generic_union_reset,
+		avro_generic_union_get_type,
+		avro_generic_union_get_schema,
+		/* primitive getters */
+		NULL, /* get_boolean */
+		NULL, /* get_bytes */
+		NULL, /* grab_bytes */
+		NULL, /* get_double */
+		NULL, /* get_float */
+		NULL, /* get_int */
+		NULL, /* get_long */
+		NULL, /* get_null */
+		NULL, /* get_string */
+		NULL, /* grab_string */
+		NULL, /* get_enum */
+		NULL, /* get_fixed */
+		NULL, /* grab_fixed */
+		/* primitive setters */
+		NULL, /* set_boolean */
+		NULL, /* set_bytes */
+		NULL, /* give_bytes */
+		NULL, /* set_double */
+		NULL, /* set_float */
+		NULL, /* set_int */
+		NULL, /* set_long */
+		NULL, /* set_null */
+		NULL, /* set_string */
+		NULL, /* set_string_length */
+		NULL, /* give_string_length */
+		NULL, /* set_enum */
+		NULL, /* set_fixed */
+		NULL, /* give_fixed */
+		/* compound getters */
+		NULL, /* get_size */
+		NULL, /* get_by_index */
+		NULL, /* get_by_name */
+		avro_generic_union_get_discriminant,
+		avro_generic_union_get_current_branch,
+		/* compound setters */
+		NULL, /* append */
+		NULL, /* add */
+		avro_generic_union_set_branch
+	},
+	avro_generic_union_instance_size,
+	avro_generic_union_init,
+	avro_generic_union_done
+};
+
+static avro_generic_value_iface_t *
+avro_generic_union_class(avro_schema_t schema, memoize_state_t *state)
+{
+	avro_generic_union_value_iface_t  *iface =
+		avro_new(avro_generic_union_value_iface_t);
+	if (iface == NULL) {
+		return NULL;
+	}
+
+	memset(iface, 0, sizeof(avro_generic_union_value_iface_t));
+	iface->parent = AVRO_GENERIC_UNION_CLASS;
+	iface->refcount = 1;
+	iface->schema = avro_schema_incref(schema);
+
+	iface->branch_count = avro_schema_union_size(schema);
+	size_t  branch_ifaces_size =
+		sizeof(avro_generic_value_iface_t *) * iface->branch_count;
+
+	iface->branch_ifaces = avro_malloc(branch_ifaces_size);
+	if (iface->branch_ifaces == NULL) {
+		goto error;
+	}
+
+	size_t  max_branch_size = 0;
+	size_t  i;
+	for (i = 0; i < iface->branch_count; i++) {
+		avro_schema_t  branch_schema =
+		    avro_schema_union_branch(schema, i);
+
+		iface->branch_ifaces[i] =
+		    avro_generic_class_from_schema_memoized(branch_schema, state);
+		if (iface->branch_ifaces[i] == NULL) {
+			goto error;
+		}
+
+		size_t  branch_size =
+		    avro_value_instance_size(iface->branch_ifaces[i]);
+		if (branch_size == 0) {
+			avro_set_error("Union branch class must provide instance_size");
+			goto error;
+		}
+
+#if DEBUG_BRANCHES
+		fprintf(stderr, "Branch %zu, size %zu\n",
+			i, branch_size);
+#endif
+
+		if (branch_size > max_branch_size) {
+			max_branch_size = branch_size;
+		}
+	}
+
+	iface->instance_size =
+		sizeof(avro_generic_union_t) + max_branch_size;
+#if DEBUG_BRANCHES
+	fprintf(stderr, "MAX BRANCH SIZE: %zu\n", max_branch_size);
+#endif
+
+	return &iface->parent;
+
+error:
+	avro_schema_decref(iface->schema);
+	if (iface->branch_ifaces != NULL) {
+		for (i = 0; i < iface->branch_count; i++) {
+			if (iface->branch_ifaces[i] != NULL) {
+				avro_value_iface_decref(&iface->branch_ifaces[i]->parent);
+			}
+		}
+		avro_free(iface->branch_ifaces, branch_ifaces_size);
+	}
+	avro_freet(avro_generic_union_value_iface_t, iface);
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * Schema type dispatcher
+ */
+
+static avro_generic_value_iface_t *
+avro_generic_class_from_schema_memoized(avro_schema_t schema,
+					memoize_state_t *state)
+{
+	/*
+	 * If we've already instantiated a value class for this schema,
+	 * just return it.
+	 */
+
+	avro_generic_value_iface_t  *result = NULL;
+	if (avro_memoize_get(&state->mem, schema, NULL, (void **) &result)) {
+		avro_value_iface_incref(&result->parent);
+		return result;
+	}
+
+	/*
+	 * Otherwise instantiate the value class based on the schema
+	 * type.
+	 */
+
+	switch (schema->type) {
+	case AVRO_BOOLEAN:
+		result = &AVRO_GENERIC_BOOLEAN_CLASS;
+		break;
+	case AVRO_BYTES:
+		result = &AVRO_GENERIC_BYTES_CLASS;
+		break;
+	case AVRO_DOUBLE:
+		result = &AVRO_GENERIC_DOUBLE_CLASS;
+		break;
+	case AVRO_FLOAT:
+		result = &AVRO_GENERIC_FLOAT_CLASS;
+		break;
+	case AVRO_INT32:
+		result = &AVRO_GENERIC_INT_CLASS;
+		break;
+	case AVRO_INT64:
+		result = &AVRO_GENERIC_LONG_CLASS;
+		break;
+	case AVRO_NULL:
+		result = &AVRO_GENERIC_NULL_CLASS;
+		break;
+	case AVRO_STRING:
+		result = &AVRO_GENERIC_STRING_CLASS;
+		break;
+
+	case AVRO_ARRAY:
+		result = avro_generic_array_class(schema, state);
+		break;
+	case AVRO_ENUM:
+		result = avro_generic_enum_class(schema);
+		break;
+	case AVRO_FIXED:
+		result = avro_generic_fixed_class(schema);
+		break;
+	case AVRO_MAP:
+		result = avro_generic_map_class(schema, state);
+		break;
+	case AVRO_RECORD:
+		result = avro_generic_record_class(schema, state);
+		break;
+	case AVRO_UNION:
+		result = avro_generic_union_class(schema, state);
+		break;
+
+	case AVRO_LINK:
+		{
+			avro_generic_link_value_iface_t  *lresult =
+			    avro_generic_link_class(schema);
+			lresult->next = state->links;
+			state->links = lresult;
+			result = &lresult->parent;
+			break;
+		}
+
+	default:
+		avro_set_error("Unknown schema type");
+		return NULL;
+	}
+
+	/*

[... 65 lines stripped ...]


Mime
View raw message