avro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dcrea...@apache.org
Subject svn commit: r1175832 [3/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
Added: avro/trunk/lang/c/src/resolved-reader.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/resolved-reader.c?rev=1175832&view=auto
==============================================================================
--- avro/trunk/lang/c/src/resolved-reader.c (added)
+++ avro/trunk/lang/c/src/resolved-reader.c Mon Sep 26 12:36:26 2011
@@ -0,0 +1,3367 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.	 You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "avro_private.h"
+#include "avro/allocation.h"
+#include "avro/basics.h"
+#include "avro/data.h"
+#include "avro/errors.h"
+#include "avro/refcount.h"
+#include "avro/resolver.h"
+#include "avro/schema.h"
+#include "avro/value.h"
+#include "st.h"
+
+#ifndef AVRO_RESOLVER_DEBUG
+#define AVRO_RESOLVER_DEBUG 0
+#endif
+
+#if AVRO_RESOLVER_DEBUG
+#include <stdio.h>
+#define DEBUG(...) \
+	do { \
+		fprintf(stderr, __VA_ARGS__); \
+		fprintf(stderr, "\n"); \
+	} while (0)
+#else
+#define DEBUG(...)  /* don't print messages */
+#endif
+
+
+typedef struct avro_resolved_reader  avro_resolved_reader_t;
+
+struct avro_resolved_reader {
+	avro_value_iface_t  parent;
+
+	/** The reference count for this interface. */
+	volatile int  refcount;
+
+	/** The writer schema. */
+	avro_schema_t  wschema;
+
+	/** The reader schema. */
+	avro_schema_t  rschema;
+
+	/* The size of the value instances for this resolver. */
+	size_t  instance_size;
+
+	/* A function to calculate the instance size once the overall
+	 * top-level resolver (and all of its children) have been
+	 * constructed. */
+	void
+	(*calculate_size)(avro_resolved_reader_t *iface);
+
+	/* A free function for this resolver */
+	void
+	(*free_iface)(avro_resolved_reader_t *iface, st_table *freeing);
+
+	/* An initialization function for instances of this resolver. */
+	int
+	(*init)(const avro_resolved_reader_t *iface, void *self);
+
+	/* A finalization function for instances of this resolver. */
+	void
+	(*done)(const avro_resolved_reader_t *iface, void *self);
+
+	/* Clear out any existing wrappers, if any */
+	int
+	(*reset_wrappers)(const avro_resolved_reader_t *iface, void *self);
+};
+
+#define avro_resolved_reader_calculate_size(iface) \
+	do { \
+		if ((iface)->calculate_size != NULL) { \
+			(iface)->calculate_size((iface)); \
+		} \
+	} while (0)
+#define avro_resolved_reader_init(iface, self) \
+	((iface)->init == NULL? 0: (iface)->init((iface), (self)))
+#define avro_resolved_reader_done(iface, self) \
+	((iface)->done == NULL? (void) 0: (iface)->done((iface), (self)))
+#define avro_resolved_reader_reset_wrappers(iface, self) \
+	((iface)->reset_wrappers == NULL? 0: \
+	 (iface)->reset_wrappers((iface), (self)))
+
+
+/*
+ * We assume that each instance type in this value contains an an
+ * avro_value_t as its first element, which is the current wrapped
+ * value.
+ */
+
+void
+avro_resolved_reader_set_source(avro_value_t *resolved,
+				avro_value_t *dest)
+{
+	avro_value_t  *self = resolved->self;
+	if (self->self != NULL) {
+		avro_value_decref(self);
+	}
+	avro_value_copy_ref(self, dest);
+}
+
+void
+avro_resolved_reader_clear_source(avro_value_t *resolved)
+{
+	avro_value_t  *self = resolved->self;
+	if (self->self != NULL) {
+		avro_value_decref(self);
+	}
+	self->iface = NULL;
+	self->self = NULL;
+}
+
+int
+avro_resolved_reader_new_value(avro_value_iface_t *viface,
+			       avro_value_t *value)
+{
+	int  rval;
+	avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	void  *self = avro_malloc(iface->instance_size + sizeof(volatile int));
+	if (self == NULL) {
+		value->iface = NULL;
+		value->self = NULL;
+		return ENOMEM;
+	}
+
+	memset(self, 0, iface->instance_size + sizeof(volatile int));
+	volatile int  *refcount = self;
+	self += sizeof(volatile int);
+
+	rval = avro_resolved_reader_init(iface, self);
+	if (rval != 0) {
+		avro_free(self, iface->instance_size + sizeof(volatile int));
+		value->iface = NULL;
+		value->self = NULL;
+		return rval;
+	}
+
+	*refcount = 1;
+	value->iface = avro_value_iface_incref(viface);
+	value->self = self;
+	return 0;
+}
+
+static void
+avro_resolved_reader_free_value(const avro_value_iface_t *viface, void *vself)
+{
+	avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	avro_value_t  *self = vself;
+
+	avro_resolved_reader_done(iface, vself);
+	if (self->self != NULL) {
+		avro_value_decref(self);
+	}
+
+	vself -= sizeof(volatile int);
+	avro_free(vself, iface->instance_size + sizeof(volatile int));
+}
+
+static void
+avro_resolved_reader_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_resolved_reader_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_resolved_reader_free_value(value->iface, value->self);
+	}
+}
+
+
+static avro_value_iface_t *
+avro_resolved_reader_incref_iface(avro_value_iface_t *viface)
+{
+	avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	avro_refcount_inc(&iface->refcount);
+	return viface;
+}
+
+static void
+free_resolver(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	/* First check if we've already started freeing this resolver. */
+	if (st_lookup(freeing, (st_data_t) iface, NULL)) {
+		DEBUG("Already freed %p", iface);
+		return;
+	}
+
+	/* Otherwise add this resolver to the freeing set, then free it. */
+	st_insert(freeing, (st_data_t) iface, (st_data_t) NULL);
+	DEBUG("Freeing resolver %p (%s->%s)", iface,
+	      avro_schema_type_name(iface->wschema),
+	      avro_schema_type_name(iface->rschema));
+
+	iface->free_iface(iface, freeing);
+}
+
+static void
+avro_resolved_reader_calculate_size_(avro_resolved_reader_t *iface)
+{
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for %s->%s",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+	iface->instance_size = sizeof(avro_value_t);
+}
+
+static void
+avro_resolved_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	AVRO_UNUSED(freeing);
+	avro_schema_decref(iface->wschema);
+	avro_schema_decref(iface->rschema);
+	avro_freet(avro_resolved_reader_t, iface);
+}
+
+static void
+avro_resolved_reader_decref_iface(avro_value_iface_t *viface)
+{
+	avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	DEBUG("Decref resolver %p (before=%d)", iface, iface->refcount);
+	if (avro_refcount_dec(&iface->refcount)) {
+		st_table  *freeing = st_init_numtable();
+		free_resolver(iface, freeing);
+		st_free_table(freeing);
+	}
+}
+
+static int
+avro_resolved_reader_reset(const avro_value_iface_t *viface, void *vself)
+{
+	/*
+	 * To reset a wrapped value, we first clear out any wrappers,
+	 * and then have the wrapped value reset itself.
+	 */
+
+	int  rval;
+	avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	avro_value_t  *self = vself;
+	check(rval, avro_resolved_reader_reset_wrappers(iface, vself));
+	return avro_value_reset(self);
+}
+
+static avro_type_t
+avro_resolved_reader_get_type(const avro_value_iface_t *viface, const void *vself)
+{
+	AVRO_UNUSED(vself);
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	return avro_typeof(iface->rschema);
+}
+
+static avro_schema_t
+avro_resolved_reader_get_schema(const avro_value_iface_t *viface, const void *vself)
+{
+	AVRO_UNUSED(vself);
+	avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	return iface->rschema;
+}
+
+
+static avro_resolved_reader_t *
+avro_resolved_reader_create(avro_schema_t wschema, avro_schema_t rschema)
+{
+	avro_resolved_reader_t  *self = avro_new(avro_resolved_reader_t);
+	memset(self, 0, sizeof(avro_resolved_reader_t));
+
+	self->parent.incref_iface = avro_resolved_reader_incref_iface;
+	self->parent.decref_iface = avro_resolved_reader_decref_iface;
+	self->parent.incref = avro_resolved_reader_incref;
+	self->parent.decref = avro_resolved_reader_decref;
+	self->parent.reset = avro_resolved_reader_reset;
+	self->parent.get_type = avro_resolved_reader_get_type;
+	self->parent.get_schema = avro_resolved_reader_get_schema;
+
+	self->refcount = 1;
+	self->wschema = avro_schema_incref(wschema);
+	self->rschema = avro_schema_incref(rschema);
+	self->calculate_size = avro_resolved_reader_calculate_size_;
+	self->free_iface = avro_resolved_reader_free_iface;
+	self->reset_wrappers = NULL;
+	return self;
+}
+
+
+/*-----------------------------------------------------------------------
+ * Memoized resolvers
+ */
+
+typedef struct avro_resolved_link_reader  avro_resolved_link_reader_t;
+
+typedef struct memoize_state_t {
+	avro_memoize_t  mem;
+	avro_resolved_link_reader_t  *links;
+} memoize_state_t;
+
+static avro_resolved_reader_t *
+avro_resolved_reader_new_memoized(memoize_state_t *state,
+				  avro_schema_t wschema, avro_schema_t rschema);
+
+
+/*-----------------------------------------------------------------------
+ * Recursive schemas
+ */
+
+/*
+ * Recursive schemas are handled specially; the value implementation for
+ * an AVRO_LINK schema is simply a wrapper around the value
+ * implementation for the link's target schema.  The value methods all
+ * delegate to the wrapped implementation.
+ *
+ * Complicating the links here is that we might be linking to the writer
+ * schema or the reader schema.  This only matters for a couple of
+ * methods, so instead of keeping a boolean flag in the value interface,
+ * we just have separate method implementations that we slot in
+ * appropriately.
+ */
+
+struct avro_resolved_link_reader {
+	avro_resolved_reader_t  parent;
+
+	/**
+	 * A pointer to the “next” link resolver that we've had to
+	 * create.  We use this as we're creating the overall top-level
+	 * resolver to keep track of which ones we have to fix up
+	 * afterwards.
+	 */
+	avro_resolved_link_reader_t  *next;
+
+	/** The target's implementation. */
+	avro_resolved_reader_t  *target_resolver;
+};
+
+typedef struct avro_resolved_link_value {
+	avro_value_t  wrapped;
+	avro_value_t  target;
+} avro_resolved_link_value_t;
+
+static void
+avro_resolved_wlink_reader_calculate_size(avro_resolved_reader_t *iface)
+{
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for [%s]->%s",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+	iface->instance_size = sizeof(avro_resolved_link_value_t);
+}
+
+static void
+avro_resolved_rlink_reader_calculate_size(avro_resolved_reader_t *iface)
+{
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for %s->[%s]",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+	iface->instance_size = sizeof(avro_resolved_link_value_t);
+}
+
+static void
+avro_resolved_link_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	avro_resolved_link_reader_t  *liface =
+	    container_of(iface, avro_resolved_link_reader_t, parent);
+	if (liface->target_resolver != NULL) {
+		free_resolver(liface->target_resolver, freeing);
+	}
+	avro_schema_decref(iface->wschema);
+	avro_schema_decref(iface->rschema);
+	avro_freet(avro_resolved_link_reader_t, iface);
+}
+
+static int
+avro_resolved_link_reader_init(const avro_resolved_reader_t *iface, void *vself)
+{
+	int  rval;
+	const avro_resolved_link_reader_t  *liface =
+	    container_of(iface, avro_resolved_link_reader_t, parent);
+	avro_resolved_link_value_t  *self = vself;
+	size_t  target_instance_size = liface->target_resolver->instance_size;
+
+	self->target.iface = &liface->target_resolver->parent;
+	self->target.self = avro_malloc(target_instance_size);
+	if (self->target.self == NULL) {
+		return ENOMEM;
+	}
+	DEBUG("Allocated <%p:%zu> for link", self->target.self, target_instance_size);
+
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+
+	rval = avro_resolved_reader_init(liface->target_resolver, self->target.self);
+	if (rval != 0) {
+		avro_free(self->target.self, target_instance_size);
+	}
+	return rval;
+}
+
+static void
+avro_resolved_link_reader_done(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_link_reader_t  *liface =
+	    container_of(iface, avro_resolved_link_reader_t, parent);
+	avro_resolved_link_value_t  *self = vself;
+	size_t  target_instance_size = liface->target_resolver->instance_size;
+	DEBUG("Freeing <%p:%zu> for link", self->target.self, target_instance_size);
+	avro_resolved_reader_done(liface->target_resolver, self->target.self);
+	avro_free(self->target.self, target_instance_size);
+	self->target.iface = NULL;
+	self->target.self = NULL;
+}
+
+static int
+avro_resolved_link_reader_reset(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_link_reader_t  *liface =
+	    container_of(iface, avro_resolved_link_reader_t, parent);
+	avro_resolved_link_value_t  *self = vself;
+	return avro_resolved_reader_reset_wrappers
+	    (liface->target_resolver, self->target.self);
+}
+
+static avro_type_t
+avro_resolved_link_reader_get_type(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_type(&self->target);
+}
+
+static avro_schema_t
+avro_resolved_link_reader_get_schema(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_schema(&self->target);
+}
+
+static int
+avro_resolved_link_reader_get_boolean(const avro_value_iface_t *iface,
+				      const void *vself, int *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_boolean(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_bytes(const avro_value_iface_t *iface,
+				    const void *vself, const void **buf, size_t *size)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_bytes(&self->target, buf, size);
+}
+
+static int
+avro_resolved_link_reader_grab_bytes(const avro_value_iface_t *iface,
+				     const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_grab_bytes(&self->target, dest);
+}
+
+static int
+avro_resolved_link_reader_get_double(const avro_value_iface_t *iface,
+				     const void *vself, double *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_double(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_float(const avro_value_iface_t *iface,
+				    const void *vself, float *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_float(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_int(const avro_value_iface_t *iface,
+				  const void *vself, int32_t *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_int(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_long(const avro_value_iface_t *iface,
+				   const void *vself, int64_t *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_long(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_null(const avro_value_iface_t *iface, const void *vself)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_null(&self->target);
+}
+
+static int
+avro_resolved_link_reader_get_string(const avro_value_iface_t *iface,
+				     const void *vself, const char **str, size_t *size)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_string(&self->target, str, size);
+}
+
+static int
+avro_resolved_link_reader_grab_string(const avro_value_iface_t *iface,
+				      const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_grab_string(&self->target, dest);
+}
+
+static int
+avro_resolved_link_reader_get_enum(const avro_value_iface_t *iface,
+				   const void *vself, int *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_enum(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_fixed(const avro_value_iface_t *iface,
+				    const void *vself, const void **buf, size_t *size)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_fixed(&self->target, buf, size);
+}
+
+static int
+avro_resolved_link_reader_grab_fixed(const avro_value_iface_t *iface,
+				     const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_grab_fixed(&self->target, dest);
+}
+
+static int
+avro_resolved_link_reader_set_boolean(const avro_value_iface_t *iface,
+				      void *vself, int val)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_boolean(&self->target, val);
+}
+
+static int
+avro_resolved_link_reader_set_bytes(const avro_value_iface_t *iface,
+				    void *vself, void *buf, size_t size)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_bytes(&self->target, buf, size);
+}
+
+static int
+avro_resolved_link_reader_give_bytes(const avro_value_iface_t *iface,
+				     void *vself, avro_wrapped_buffer_t *buf)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_give_bytes(&self->target, buf);
+}
+
+static int
+avro_resolved_link_reader_set_double(const avro_value_iface_t *iface,
+				     void *vself, double val)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_double(&self->target, val);
+}
+
+static int
+avro_resolved_link_reader_set_float(const avro_value_iface_t *iface,
+				    void *vself, float val)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_float(&self->target, val);
+}
+
+static int
+avro_resolved_link_reader_set_int(const avro_value_iface_t *iface,
+				  void *vself, int32_t val)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_int(&self->target, val);
+}
+
+static int
+avro_resolved_link_reader_set_long(const avro_value_iface_t *iface,
+				   void *vself, int64_t val)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_long(&self->target, val);
+}
+
+static int
+avro_resolved_link_reader_set_null(const avro_value_iface_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_null(&self->target);
+}
+
+static int
+avro_resolved_link_reader_set_string(const avro_value_iface_t *iface,
+				     void *vself, char *str)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_string(&self->target, str);
+}
+
+static int
+avro_resolved_link_reader_set_string_len(const avro_value_iface_t *iface,
+					 void *vself, char *str, size_t size)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_string_len(&self->target, str, size);
+}
+
+static int
+avro_resolved_link_reader_give_string_len(const avro_value_iface_t *iface,
+					  void *vself, avro_wrapped_buffer_t *buf)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_give_string_len(&self->target, buf);
+}
+
+static int
+avro_resolved_link_reader_set_enum(const avro_value_iface_t *iface,
+				   void *vself, int val)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_enum(&self->target, val);
+}
+
+static int
+avro_resolved_link_reader_set_fixed(const avro_value_iface_t *iface,
+				    void *vself, void *buf, size_t size)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_fixed(&self->target, buf, size);
+}
+
+static int
+avro_resolved_link_reader_give_fixed(const avro_value_iface_t *iface,
+				     void *vself, avro_wrapped_buffer_t *buf)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_give_fixed(&self->target, buf);
+}
+
+static int
+avro_resolved_link_reader_get_size(const avro_value_iface_t *iface,
+				   const void *vself, size_t *size)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_size(&self->target, size);
+}
+
+static int
+avro_resolved_link_reader_get_by_index(const avro_value_iface_t *iface,
+				       const void *vself, size_t index,
+				       avro_value_t *child, const char **name)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_by_index(&self->target, index, child, name);
+}
+
+static int
+avro_resolved_link_reader_get_by_name(const avro_value_iface_t *iface,
+				      const void *vself, const char *name,
+				      avro_value_t *child, size_t *index)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_by_name(&self->target, name, child, index);
+}
+
+static int
+avro_resolved_link_reader_get_discriminant(const avro_value_iface_t *iface,
+					   const void *vself, int *out)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_discriminant(&self->target, out);
+}
+
+static int
+avro_resolved_link_reader_get_current_branch(const avro_value_iface_t *iface,
+					     const void *vself, avro_value_t *branch)
+{
+	AVRO_UNUSED(iface);
+	const avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_get_current_branch(&self->target, branch);
+}
+
+static int
+avro_resolved_link_reader_append(const avro_value_iface_t *iface,
+				 void *vself, avro_value_t *child_out,
+				 size_t *new_index)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_append(&self->target, child_out, new_index);
+}
+
+static int
+avro_resolved_link_reader_add(const avro_value_iface_t *iface,
+			      void *vself, const char *key,
+			      avro_value_t *child, size_t *index, int *is_new)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_add(&self->target, key, child, index, is_new);
+}
+
+static int
+avro_resolved_link_reader_set_branch(const avro_value_iface_t *iface,
+				     void *vself, int discriminant,
+				     avro_value_t *branch)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_link_value_t  *self = vself;
+	avro_value_t  *target_vself = self->target.self;
+	*target_vself = self->wrapped;
+	return avro_value_set_branch(&self->target, discriminant, branch);
+}
+
+static avro_resolved_link_reader_t *
+avro_resolved_link_reader_create(avro_schema_t wschema, avro_schema_t rschema)
+{
+	avro_resolved_reader_t  *self = avro_new(avro_resolved_link_reader_t);
+	memset(self, 0, sizeof(avro_resolved_link_reader_t));
+
+	self->parent.incref_iface = avro_resolved_reader_incref_iface;
+	self->parent.decref_iface = avro_resolved_reader_decref_iface;
+	self->parent.incref = avro_resolved_reader_incref;
+	self->parent.decref = avro_resolved_reader_decref;
+	self->parent.reset = avro_resolved_reader_reset;
+	self->parent.get_type = avro_resolved_link_reader_get_type;
+	self->parent.get_schema = avro_resolved_link_reader_get_schema;
+	self->parent.get_size = avro_resolved_link_reader_get_size;
+	self->parent.get_by_index = avro_resolved_link_reader_get_by_index;
+	self->parent.get_by_name = avro_resolved_link_reader_get_by_name;
+
+	self->refcount = 1;
+	self->wschema = avro_schema_incref(wschema);
+	self->rschema = avro_schema_incref(rschema);
+	self->free_iface = avro_resolved_link_reader_free_iface;
+	self->init = avro_resolved_link_reader_init;
+	self->done = avro_resolved_link_reader_done;
+	self->reset_wrappers = avro_resolved_link_reader_reset;
+
+	self->parent.get_boolean = avro_resolved_link_reader_get_boolean;
+	self->parent.get_bytes = avro_resolved_link_reader_get_bytes;
+	self->parent.grab_bytes = avro_resolved_link_reader_grab_bytes;
+	self->parent.get_double = avro_resolved_link_reader_get_double;
+	self->parent.get_float = avro_resolved_link_reader_get_float;
+	self->parent.get_int = avro_resolved_link_reader_get_int;
+	self->parent.get_long = avro_resolved_link_reader_get_long;
+	self->parent.get_null = avro_resolved_link_reader_get_null;
+	self->parent.get_string = avro_resolved_link_reader_get_string;
+	self->parent.grab_string = avro_resolved_link_reader_grab_string;
+	self->parent.get_enum = avro_resolved_link_reader_get_enum;
+	self->parent.get_fixed = avro_resolved_link_reader_get_fixed;
+	self->parent.grab_fixed = avro_resolved_link_reader_grab_fixed;
+
+	self->parent.set_boolean = avro_resolved_link_reader_set_boolean;
+	self->parent.set_bytes = avro_resolved_link_reader_set_bytes;
+	self->parent.give_bytes = avro_resolved_link_reader_give_bytes;
+	self->parent.set_double = avro_resolved_link_reader_set_double;
+	self->parent.set_float = avro_resolved_link_reader_set_float;
+	self->parent.set_int = avro_resolved_link_reader_set_int;
+	self->parent.set_long = avro_resolved_link_reader_set_long;
+	self->parent.set_null = avro_resolved_link_reader_set_null;
+	self->parent.set_string = avro_resolved_link_reader_set_string;
+	self->parent.set_string_len = avro_resolved_link_reader_set_string_len;
+	self->parent.give_string_len = avro_resolved_link_reader_give_string_len;
+	self->parent.set_enum = avro_resolved_link_reader_set_enum;
+	self->parent.set_fixed = avro_resolved_link_reader_set_fixed;
+	self->parent.give_fixed = avro_resolved_link_reader_give_fixed;
+
+	self->parent.get_size = avro_resolved_link_reader_get_size;
+	self->parent.get_by_index = avro_resolved_link_reader_get_by_index;
+	self->parent.get_by_name = avro_resolved_link_reader_get_by_name;
+	self->parent.get_discriminant = avro_resolved_link_reader_get_discriminant;
+	self->parent.get_current_branch = avro_resolved_link_reader_get_current_branch;
+
+	self->parent.append = avro_resolved_link_reader_append;
+	self->parent.add = avro_resolved_link_reader_add;
+	self->parent.set_branch = avro_resolved_link_reader_set_branch;
+
+	return container_of(self, avro_resolved_link_reader_t, parent);
+}
+
+static avro_resolved_reader_t *
+try_wlink(memoize_state_t *state,
+	  avro_schema_t wschema, avro_schema_t rschema)
+{
+	AVRO_UNUSED(rschema);
+
+	/*
+	 * For link schemas, we create a special value implementation
+	 * that allocates space for its wrapped value at runtime.  This
+	 * lets us handle recursive types without having to instantiate
+	 * in infinite-size value.
+	 */
+
+	avro_schema_t  wtarget = avro_schema_link_target(wschema);
+	avro_resolved_link_reader_t  *lself =
+	    avro_resolved_link_reader_create(wtarget, rschema);
+	avro_memoize_set(&state->mem, wschema, rschema, lself);
+
+	avro_resolved_reader_t  *target_resolver =
+	    avro_resolved_reader_new_memoized(state, wtarget, rschema);
+	if (target_resolver == NULL) {
+		avro_memoize_delete(&state->mem, wschema, rschema);
+		avro_value_iface_decref(&lself->parent.parent);
+		avro_prefix_error("Link target isn't compatible: ");
+		DEBUG("%s", avro_strerror());
+		return NULL;
+	}
+
+	lself->parent.calculate_size = avro_resolved_wlink_reader_calculate_size;
+	lself->target_resolver = target_resolver;
+	lself->next = state->links;
+	state->links = lself;
+
+	return &lself->parent;
+}
+
+static avro_resolved_reader_t *
+try_rlink(memoize_state_t *state,
+	  avro_schema_t wschema, avro_schema_t rschema)
+{
+	AVRO_UNUSED(rschema);
+
+	/*
+	 * For link schemas, we create a special value implementation
+	 * that allocates space for its wrapped value at runtime.  This
+	 * lets us handle recursive types without having to instantiate
+	 * in infinite-size value.
+	 */
+
+	avro_schema_t  rtarget = avro_schema_link_target(rschema);
+	avro_resolved_link_reader_t  *lself =
+	    avro_resolved_link_reader_create(wschema, rtarget);
+	avro_memoize_set(&state->mem, wschema, rschema, lself);
+
+	avro_resolved_reader_t  *target_resolver =
+	    avro_resolved_reader_new_memoized(state, wschema, rtarget);
+	if (target_resolver == NULL) {
+		avro_memoize_delete(&state->mem, wschema, rschema);
+		avro_value_iface_decref(&lself->parent.parent);
+		avro_prefix_error("Link target isn't compatible: ");
+		DEBUG("%s", avro_strerror());
+		return NULL;
+	}
+
+	lself->parent.calculate_size = avro_resolved_rlink_reader_calculate_size;
+	lself->target_resolver = target_resolver;
+	lself->next = state->links;
+	state->links = lself;
+
+	return &lself->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * boolean
+ */
+
+static int
+avro_resolved_reader_get_boolean(const avro_value_iface_t *viface,
+				 const void *vself, int *val)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting boolean from %p", src->self);
+	return avro_value_get_boolean(src, val);
+}
+
+static avro_resolved_reader_t *
+try_boolean(memoize_state_t *state,
+	    avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_boolean(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_boolean = avro_resolved_reader_get_boolean;
+		return self;
+	}
+	avro_set_error("Writer %s not compatible with reader boolean",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * bytes
+ */
+
+static int
+avro_resolved_reader_get_bytes(const avro_value_iface_t *viface,
+			       const void *vself, const void **buf, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting bytes from %p", src->self);
+	return avro_value_get_bytes(src, buf, size);
+}
+
+static int
+avro_resolved_reader_grab_bytes(const avro_value_iface_t *viface,
+				const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Grabbing bytes from %p", src->self);
+	return avro_value_grab_bytes(src, dest);
+}
+
+static avro_resolved_reader_t *
+try_bytes(memoize_state_t *state,
+	  avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_bytes(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_bytes = avro_resolved_reader_get_bytes;
+		self->parent.grab_bytes = avro_resolved_reader_grab_bytes;
+		return self;
+	}
+	avro_set_error("Writer %s not compatible with reader bytes",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * double
+ */
+
+static int
+avro_resolved_reader_get_double(const avro_value_iface_t *viface,
+				const void *vself, double *val)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting double from %p", src->self);
+	return avro_value_get_double(src, val);
+}
+
+static int
+avro_resolved_reader_get_double_float(const avro_value_iface_t *viface,
+				      const void *vself, double *val)
+{
+	int  rval;
+	float  real_val;
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Promoting double from float %p", src->self);
+	check(rval, avro_value_get_float(src, &real_val));
+	*val = real_val;
+	return 0;
+}
+
+static int
+avro_resolved_reader_get_double_int(const avro_value_iface_t *viface,
+				    const void *vself, double *val)
+{
+	int  rval;
+	int32_t  real_val;
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Promoting double from int %p", src->self);
+	check(rval, avro_value_get_int(src, &real_val));
+	*val = real_val;
+	return 0;
+}
+
+static int
+avro_resolved_reader_get_double_long(const avro_value_iface_t *viface,
+				     const void *vself, double *val)
+{
+	int  rval;
+	int64_t  real_val;
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Promoting double from long %p", src->self);
+	check(rval, avro_value_get_long(src, &real_val));
+	*val = real_val;
+	return 0;
+}
+
+static avro_resolved_reader_t *
+try_double(memoize_state_t *state,
+	   avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_double(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_double = avro_resolved_reader_get_double;
+		return self;
+	}
+
+	else if (is_avro_float(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_double = avro_resolved_reader_get_double_float;
+		return self;
+	}
+
+	else if (is_avro_int32(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_double = avro_resolved_reader_get_double_int;
+		return self;
+	}
+
+	else if (is_avro_int64(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_double = avro_resolved_reader_get_double_long;
+		return self;
+	}
+
+	avro_set_error("Writer %s not compatible with reader double",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * float
+ */
+
+static int
+avro_resolved_reader_get_float(const avro_value_iface_t *viface,
+			       const void *vself, float *val)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting float from %p", src->self);
+	return avro_value_get_float(src, val);
+}
+
+static int
+avro_resolved_reader_get_float_int(const avro_value_iface_t *viface,
+				   const void *vself, float *val)
+{
+	int  rval;
+	int32_t  real_val;
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Promoting float from int %p", src->self);
+	check(rval, avro_value_get_int(src, &real_val));
+	*val = real_val;
+	return 0;
+}
+
+static int
+avro_resolved_reader_get_float_long(const avro_value_iface_t *viface,
+				    const void *vself, float *val)
+{
+	int  rval;
+	int64_t  real_val;
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Promoting float from long %p", src->self);
+	check(rval, avro_value_get_long(src, &real_val));
+	*val = real_val;
+	return 0;
+}
+
+static avro_resolved_reader_t *
+try_float(memoize_state_t *state,
+	  avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_float(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_float = avro_resolved_reader_get_float;
+		return self;
+	}
+
+	else if (is_avro_int32(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_float = avro_resolved_reader_get_float_int;
+		return self;
+	}
+
+	else if (is_avro_int64(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_float = avro_resolved_reader_get_float_long;
+		return self;
+	}
+
+	avro_set_error("Writer %s not compatible with reader float",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * int
+ */
+
+static int
+avro_resolved_reader_get_int(const avro_value_iface_t *viface,
+			     const void *vself, int32_t *val)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting int from %p", src->self);
+	return avro_value_get_int(src, val);
+}
+
+static avro_resolved_reader_t *
+try_int(memoize_state_t *state,
+	avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_int32(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_int = avro_resolved_reader_get_int;
+		return self;
+	}
+	avro_set_error("Writer %s not compatible with reader int",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * long
+ */
+
+static int
+avro_resolved_reader_get_long(const avro_value_iface_t *viface,
+			      const void *vself, int64_t *val)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting long from %p", src->self);
+	return avro_value_get_long(src, val);
+}
+
+static int
+avro_resolved_reader_get_long_int(const avro_value_iface_t *viface,
+				  const void *vself, int64_t *val)
+{
+	int  rval;
+	int32_t  real_val;
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Promoting long from int %p", src->self);
+	check(rval, avro_value_get_int(src, &real_val));
+	*val = real_val;
+	return 0;
+}
+
+static avro_resolved_reader_t *
+try_long(memoize_state_t *state,
+	 avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_int64(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_long = avro_resolved_reader_get_long;
+		return self;
+	}
+
+	else if (is_avro_int32(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_long = avro_resolved_reader_get_long_int;
+		return self;
+	}
+
+	avro_set_error("Writer %s not compatible with reader long",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * null
+ */
+
+static int
+avro_resolved_reader_get_null(const avro_value_iface_t *viface,
+			      const void *vself)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting null from %p", src->self);
+	return avro_value_get_null(src);
+}
+
+static avro_resolved_reader_t *
+try_null(memoize_state_t *state,
+	 avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_null(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_null = avro_resolved_reader_get_null;
+		return self;
+	}
+	avro_set_error("Writer %s not compatible with reader null",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * string
+ */
+
+static int
+avro_resolved_reader_get_string(const avro_value_iface_t *viface,
+				const void *vself, const char **str, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting string from %p", src->self);
+	return avro_value_get_string(src, str, size);
+}
+
+static int
+avro_resolved_reader_grab_string(const avro_value_iface_t *viface,
+				 const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Grabbing string from %p", src->self);
+	return avro_value_grab_string(src, dest);
+}
+
+static avro_resolved_reader_t *
+try_string(memoize_state_t *state,
+	   avro_schema_t wschema, avro_schema_t rschema)
+{
+	if (is_avro_string(wschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_string = avro_resolved_reader_get_string;
+		self->parent.grab_string = avro_resolved_reader_grab_string;
+		return self;
+	}
+	avro_set_error("Writer %s not compatible with reader string",
+		       avro_schema_type_name(wschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * array
+ */
+
+typedef struct avro_resolved_array_reader {
+	avro_resolved_reader_t  parent;
+	avro_resolved_reader_t  *child_resolver;
+} avro_resolved_array_reader_t;
+
+typedef struct avro_resolved_array_value {
+	avro_value_t  wrapped;
+	avro_raw_array_t  children;
+} avro_resolved_array_value_t;
+
+static void
+avro_resolved_array_reader_calculate_size(avro_resolved_reader_t *iface)
+{
+	avro_resolved_array_reader_t  *aiface =
+	    container_of(iface, avro_resolved_array_reader_t, parent);
+
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for %s->%s",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+	iface->instance_size = sizeof(avro_resolved_array_value_t);
+
+	avro_resolved_reader_calculate_size(aiface->child_resolver);
+}
+
+static void
+avro_resolved_array_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	avro_resolved_array_reader_t  *aiface =
+	    container_of(iface, avro_resolved_array_reader_t, parent);
+	free_resolver(aiface->child_resolver, freeing);
+	avro_schema_decref(iface->wschema);
+	avro_schema_decref(iface->rschema);
+	avro_freet(avro_resolved_array_reader_t, iface);
+}
+
+static int
+avro_resolved_array_reader_init(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_array_reader_t  *aiface =
+	    container_of(iface, avro_resolved_array_reader_t, parent);
+	avro_resolved_array_value_t  *self = vself;
+	size_t  child_instance_size = aiface->child_resolver->instance_size;
+	DEBUG("Initializing child array (child_size=%zu)", child_instance_size);
+	avro_raw_array_init(&self->children, child_instance_size);
+	return 0;
+}
+
+static void
+avro_resolved_array_reader_free_elements(const avro_resolved_reader_t *child_iface,
+					 avro_resolved_array_value_t *self)
+{
+	size_t  i;
+	for (i = 0; i < avro_raw_array_size(&self->children); i++) {
+		void  *child_self = avro_raw_array_get_raw(&self->children, i);
+		avro_resolved_reader_done(child_iface, child_self);
+	}
+}
+
+static void
+avro_resolved_array_reader_done(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_array_reader_t  *aiface =
+	    container_of(iface, avro_resolved_array_reader_t, parent);
+	avro_resolved_array_value_t  *self = vself;
+	avro_resolved_array_reader_free_elements(aiface->child_resolver, self);
+	avro_raw_array_done(&self->children);
+}
+
+static int
+avro_resolved_array_reader_reset(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_array_reader_t  *aiface =
+	    container_of(iface, avro_resolved_array_reader_t, parent);
+	avro_resolved_array_value_t  *self = vself;
+
+	/* Clear out our cache of wrapped children */
+	avro_resolved_array_reader_free_elements(aiface->child_resolver, self);
+	return 0;
+}
+
+static int
+avro_resolved_array_reader_get_size(const avro_value_iface_t *viface,
+				    const void *vself, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_resolved_array_value_t  *self = vself;
+	return avro_value_get_size(&self->wrapped, size);
+}
+
+static int
+avro_resolved_array_reader_get_by_index(const avro_value_iface_t *viface,
+					const void *vself, size_t index,
+					avro_value_t *child, const char **name)
+{
+	int  rval;
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	const avro_resolved_array_reader_t  *aiface =
+	    container_of(iface, avro_resolved_array_reader_t, parent);
+	avro_resolved_array_value_t  *self = (avro_resolved_array_value_t *) vself;
+
+	/*
+	 * Ensure that our child wrapper array is big enough to hold
+	 * this many elements.
+	 */
+	check(rval, avro_raw_array_ensure_size0(&self->children, index+1));
+	if (avro_raw_array_size(&self->children) <= index) {
+		avro_raw_array_size(&self->children) = index+1;
+	}
+
+	child->iface = &aiface->child_resolver->parent;
+	child->self = avro_raw_array_get_raw(&self->children, index);
+
+	DEBUG("Getting element %zu from array %p", index, self->wrapped.self);
+	return avro_value_get_by_index(&self->wrapped, index, child->self, name);
+}
+
+static avro_resolved_array_reader_t *
+avro_resolved_array_reader_create(avro_schema_t wschema, avro_schema_t rschema)
+{
+	avro_resolved_reader_t  *self = avro_new(avro_resolved_array_reader_t);
+	memset(self, 0, sizeof(avro_resolved_array_reader_t));
+
+	self->parent.incref_iface = avro_resolved_reader_incref_iface;
+	self->parent.decref_iface = avro_resolved_reader_decref_iface;
+	self->parent.incref = avro_resolved_reader_incref;
+	self->parent.decref = avro_resolved_reader_decref;
+	self->parent.reset = avro_resolved_reader_reset;
+	self->parent.get_type = avro_resolved_reader_get_type;
+	self->parent.get_schema = avro_resolved_reader_get_schema;
+	self->parent.get_size = avro_resolved_array_reader_get_size;
+	self->parent.get_by_index = avro_resolved_array_reader_get_by_index;
+
+	self->refcount = 1;
+	self->wschema = avro_schema_incref(wschema);
+	self->rschema = avro_schema_incref(rschema);
+	self->calculate_size = avro_resolved_array_reader_calculate_size;
+	self->free_iface = avro_resolved_array_reader_free_iface;
+	self->init = avro_resolved_array_reader_init;
+	self->done = avro_resolved_array_reader_done;
+	self->reset_wrappers = avro_resolved_array_reader_reset;
+	return container_of(self, avro_resolved_array_reader_t, parent);
+}
+
+static avro_resolved_reader_t *
+try_array(memoize_state_t *state,
+	  avro_schema_t wschema, avro_schema_t rschema)
+{
+	/*
+	 * First verify that the writer is an array.
+	 */
+
+	if (!is_avro_array(wschema)) {
+		return 0;
+	}
+
+	/*
+	 * Array schemas have to have compatible element schemas to be
+	 * compatible themselves.  Try to create an resolver to check
+	 * the compatibility.
+	 */
+
+	avro_resolved_array_reader_t  *aself =
+	    avro_resolved_array_reader_create(wschema, rschema);
+	avro_memoize_set(&state->mem, wschema, rschema, aself);
+
+	avro_schema_t  witems = avro_schema_array_items(wschema);
+	avro_schema_t  ritems = avro_schema_array_items(rschema);
+
+	avro_resolved_reader_t  *item_resolver =
+	    avro_resolved_reader_new_memoized(state, witems, ritems);
+	if (item_resolver == NULL) {
+		avro_memoize_delete(&state->mem, wschema, rschema);
+		avro_value_iface_decref(&aself->parent.parent);
+		avro_prefix_error("Array values aren't compatible: ");
+		return NULL;
+	}
+
+	/*
+	 * The two schemas are compatible.  Store the item schema's
+	 * resolver into the child_resolver field.
+	 */
+
+	aself->child_resolver = item_resolver;
+	return &aself->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * enum
+ */
+
+static int
+avro_resolved_reader_get_enum(const avro_value_iface_t *viface,
+			      const void *vself, int *val)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting enum from %p", src->self);
+	return avro_value_get_enum(src, val);
+}
+
+static avro_resolved_reader_t *
+try_enum(memoize_state_t *state,
+	 avro_schema_t wschema, avro_schema_t rschema)
+{
+	/*
+	 * Enum schemas have to have the same name — but not the same
+	 * list of symbols — to be compatible.
+	 */
+
+	if (is_avro_enum(wschema)) {
+		const char  *wname = avro_schema_name(wschema);
+		const char  *rname = avro_schema_name(rschema);
+
+		if (strcmp(wname, rname) == 0) {
+			avro_resolved_reader_t  *self =
+			    avro_resolved_reader_create(wschema, rschema);
+			avro_memoize_set(&state->mem, wschema, rschema, self);
+			self->parent.get_enum = avro_resolved_reader_get_enum;
+			return self;
+		}
+	}
+	avro_set_error("Writer %s not compatible with reader %s",
+		       avro_schema_type_name(wschema),
+		       avro_schema_type_name(rschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * fixed
+ */
+
+static int
+avro_resolved_reader_get_fixed(const avro_value_iface_t *viface,
+			       const void *vself, const void **buf, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Getting fixed from %p", vself);
+	return avro_value_get_fixed(src, buf, size);
+}
+
+static int
+avro_resolved_reader_grab_fixed(const avro_value_iface_t *viface,
+				const void *vself, avro_wrapped_buffer_t *dest)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	DEBUG("Grabbing fixed from %p", vself);
+	return avro_value_grab_fixed(src, dest);
+}
+
+static avro_resolved_reader_t *
+try_fixed(memoize_state_t *state,
+	  avro_schema_t wschema, avro_schema_t rschema)
+{
+	/*
+	 * Fixed schemas need the same name and size to be compatible.
+	 */
+
+	if (avro_schema_equal(wschema, rschema)) {
+		avro_resolved_reader_t  *self =
+		    avro_resolved_reader_create(wschema, rschema);
+		avro_memoize_set(&state->mem, wschema, rschema, self);
+		self->parent.get_fixed = avro_resolved_reader_get_fixed;
+		self->parent.grab_fixed = avro_resolved_reader_grab_fixed;
+		return self;
+	}
+	avro_set_error("Writer %s not compatible with reader %s",
+		       avro_schema_type_name(wschema),
+		       avro_schema_type_name(rschema));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * map
+ */
+
+typedef struct avro_resolved_map_reader {
+	avro_resolved_reader_t  parent;
+	avro_resolved_reader_t  *child_resolver;
+} avro_resolved_map_reader_t;
+
+typedef struct avro_resolved_map_value {
+	avro_value_t  wrapped;
+	avro_raw_array_t  children;
+} avro_resolved_map_value_t;
+
+static void
+avro_resolved_map_reader_calculate_size(avro_resolved_reader_t *iface)
+{
+	avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for %s->%s",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+	iface->instance_size = sizeof(avro_resolved_map_value_t);
+
+	avro_resolved_reader_calculate_size(miface->child_resolver);
+}
+
+static void
+avro_resolved_map_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+	free_resolver(miface->child_resolver, freeing);
+	avro_schema_decref(iface->wschema);
+	avro_schema_decref(iface->rschema);
+	avro_freet(avro_resolved_map_reader_t, iface);
+}
+
+static int
+avro_resolved_map_reader_init(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+	avro_resolved_map_value_t  *self = vself;
+	size_t  child_instance_size = miface->child_resolver->instance_size;
+	DEBUG("Initializing child array for map (child_size=%zu)", child_instance_size);
+	avro_raw_array_init(&self->children, child_instance_size);
+	return 0;
+}
+
+static void
+avro_resolved_map_reader_free_elements(const avro_resolved_reader_t *child_iface,
+				       avro_resolved_map_value_t *self)
+{
+	size_t  i;
+	for (i = 0; i < avro_raw_array_size(&self->children); i++) {
+		void  *child_self = avro_raw_array_get_raw(&self->children, i);
+		avro_resolved_reader_done(child_iface, child_self);
+	}
+}
+
+static void
+avro_resolved_map_reader_done(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+	avro_resolved_map_value_t  *self = vself;
+	avro_resolved_map_reader_free_elements(miface->child_resolver, self);
+	avro_raw_array_done(&self->children);
+}
+
+static int
+avro_resolved_map_reader_reset(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+	avro_resolved_map_value_t  *self = vself;
+
+	/* Clear out our cache of wrapped children */
+	avro_resolved_map_reader_free_elements(miface->child_resolver, self);
+	return 0;
+}
+
+static int
+avro_resolved_map_reader_get_size(const avro_value_iface_t *viface,
+				  const void *vself, size_t *size)
+{
+	AVRO_UNUSED(viface);
+	const avro_value_t  *src = vself;
+	return avro_value_get_size(src, size);
+}
+
+static int
+avro_resolved_map_reader_get_by_index(const avro_value_iface_t *viface,
+				      const void *vself, size_t index,
+				      avro_value_t *child, const char **name)
+{
+	int  rval;
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	const avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+	avro_resolved_map_value_t  *self = (avro_resolved_map_value_t *) vself;
+
+	/*
+	 * Ensure that our child wrapper array is big enough to hold
+	 * this many elements.
+	 */
+	check(rval, avro_raw_array_ensure_size0(&self->children, index+1));
+	if (avro_raw_array_size(&self->children) <= index) {
+		avro_raw_array_size(&self->children) = index+1;
+	}
+
+	child->iface = &miface->child_resolver->parent;
+	child->self = avro_raw_array_get_raw(&self->children, index);
+
+	DEBUG("Getting element %zu from map %p", index, self->wrapped.self);
+	return avro_value_get_by_index(&self->wrapped, index, child->self, name);
+}
+
+static int
+avro_resolved_map_reader_get_by_name(const avro_value_iface_t *viface,
+				     const void *vself, const char *name,
+				     avro_value_t *child, size_t *index)
+{
+	int  rval;
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	const avro_resolved_map_reader_t  *miface =
+	    container_of(iface, avro_resolved_map_reader_t, parent);
+	avro_resolved_map_value_t  *self = (avro_resolved_map_value_t *) vself;
+
+	/*
+	 * This is a bit convoluted.  We need to stash the wrapped child
+	 * value somewhere in our children array.  But we don't know
+	 * where to put it until the wrapped map tells us what its index
+	 * is.
+	 */
+
+	avro_value_t  real_child;
+	size_t  real_index;
+
+	DEBUG("Getting element %s from map %p", name, self->wrapped.self);
+	check(rval, avro_value_get_by_name
+	      (&self->wrapped, name, &real_child, &real_index));
+
+	/*
+	 * Ensure that our child wrapper array is big enough to hold
+	 * this many elements.
+	 */
+	check(rval, avro_raw_array_ensure_size0(&self->children, real_index+1));
+	if (avro_raw_array_size(&self->children) <= real_index) {
+		avro_raw_array_size(&self->children) = real_index+1;
+	}
+
+	child->iface = &miface->child_resolver->parent;
+	child->self = avro_raw_array_get_raw(&self->children, real_index);
+	avro_value_t  *child_vself = child->self;
+	*child_vself = real_child;
+
+	if (index != NULL) {
+		*index = real_index;
+	}
+	return 0;
+}
+
+static avro_resolved_map_reader_t *
+avro_resolved_map_reader_create(avro_schema_t wschema, avro_schema_t rschema)
+{
+	avro_resolved_reader_t  *self = avro_new(avro_resolved_map_reader_t);
+	memset(self, 0, sizeof(avro_resolved_map_reader_t));
+
+	self->parent.incref_iface = avro_resolved_reader_incref_iface;
+	self->parent.decref_iface = avro_resolved_reader_decref_iface;
+	self->parent.incref = avro_resolved_reader_incref;
+	self->parent.decref = avro_resolved_reader_decref;
+	self->parent.reset = avro_resolved_reader_reset;
+	self->parent.get_type = avro_resolved_reader_get_type;
+	self->parent.get_schema = avro_resolved_reader_get_schema;
+	self->parent.get_size = avro_resolved_map_reader_get_size;
+	self->parent.get_by_index = avro_resolved_map_reader_get_by_index;
+	self->parent.get_by_name = avro_resolved_map_reader_get_by_name;
+
+	self->refcount = 1;
+	self->wschema = avro_schema_incref(wschema);
+	self->rschema = avro_schema_incref(rschema);
+	self->calculate_size = avro_resolved_map_reader_calculate_size;
+	self->free_iface = avro_resolved_map_reader_free_iface;
+	self->init = avro_resolved_map_reader_init;
+	self->done = avro_resolved_map_reader_done;
+	self->reset_wrappers = avro_resolved_map_reader_reset;
+	return container_of(self, avro_resolved_map_reader_t, parent);
+}
+
+static avro_resolved_reader_t *
+try_map(memoize_state_t *state,
+	avro_schema_t wschema, avro_schema_t rschema)
+{
+	/*
+	 * First verify that the reader is an map.
+	 */
+
+	if (!is_avro_map(wschema)) {
+		return 0;
+	}
+
+	/*
+	 * Map schemas have to have compatible element schemas to be
+	 * compatible themselves.  Try to create an resolver to check
+	 * the compatibility.
+	 */
+
+	avro_resolved_map_reader_t  *mself =
+	    avro_resolved_map_reader_create(wschema, rschema);
+	avro_memoize_set(&state->mem, wschema, rschema, mself);
+
+	avro_schema_t  witems = avro_schema_map_values(wschema);
+	avro_schema_t  ritems = avro_schema_map_values(rschema);
+
+	avro_resolved_reader_t  *item_resolver =
+	    avro_resolved_reader_new_memoized(state, witems, ritems);
+	if (item_resolver == NULL) {
+		avro_memoize_delete(&state->mem, wschema, rschema);
+		avro_value_iface_decref(&mself->parent.parent);
+		avro_prefix_error("Map values aren't compatible: ");
+		return NULL;
+	}
+
+	/*
+	 * The two schemas are compatible.  Store the item schema's
+	 * resolver into the child_resolver field.
+	 */
+
+	mself->child_resolver = item_resolver;
+	return &mself->parent;
+}
+
+
+/*-----------------------------------------------------------------------
+ * record
+ */
+
+typedef struct avro_resolved_record_reader {
+	avro_resolved_reader_t  parent;
+	size_t  field_count;
+	size_t  *field_offsets;
+	avro_resolved_reader_t  **field_resolvers;
+	size_t  *index_mapping;
+} avro_resolved_record_reader_t;
+
+typedef struct avro_resolved_record_value {
+	avro_value_t  wrapped;
+	/* The rest of the struct is taken up by the inline storage
+	 * needed for each field. */
+} avro_resolved_record_value_t;
+
+/** Return a pointer to the given field within a record struct. */
+#define avro_resolved_record_field(iface, rec, index) \
+	(((void *) (rec)) + (iface)->field_offsets[(index)])
+
+
+static void
+avro_resolved_record_reader_calculate_size(avro_resolved_reader_t *iface)
+{
+	avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for %s->%s",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+
+	/*
+	 * Once we've figured out which reader fields we actually need,
+	 * calculate an offset for each one.
+	 */
+
+	size_t  ri;
+	size_t  next_offset = sizeof(avro_resolved_record_value_t);
+	for (ri = 0; ri < riface->field_count; ri++) {
+		riface->field_offsets[ri] = next_offset;
+		if (riface->field_resolvers[ri] != NULL) {
+			avro_resolved_reader_calculate_size
+			    (riface->field_resolvers[ri]);
+			size_t  field_size =
+			    riface->field_resolvers[ri]->instance_size;
+			DEBUG("Field %zu has size %zu", ri, field_size);
+			next_offset += field_size;
+		} else {
+			DEBUG("Field %zu is being skipped", ri);
+		}
+	}
+
+	DEBUG("Record has size %zu", next_offset);
+	iface->instance_size = next_offset;
+}
+
+
+static void
+avro_resolved_record_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+
+	if (riface->field_offsets != NULL) {
+		avro_free(riface->field_offsets,
+			  riface->field_count * sizeof(size_t));
+	}
+
+	if (riface->field_resolvers != NULL) {
+		size_t  i;
+		for (i = 0; i < riface->field_count; i++) {
+			if (riface->field_resolvers[i] != NULL) {
+				DEBUG("Freeing field %zu %p", i,
+				      riface->field_resolvers[i]);
+				free_resolver(riface->field_resolvers[i], freeing);
+			}
+		}
+		avro_free(riface->field_resolvers,
+			  riface->field_count * sizeof(avro_resolved_reader_t *));
+	}
+
+	if (riface->index_mapping != NULL) {
+		avro_free(riface->index_mapping,
+			  riface->field_count * sizeof(size_t));
+	}
+
+	avro_schema_decref(iface->wschema);
+	avro_schema_decref(iface->rschema);
+	avro_freet(avro_resolved_record_reader_t, iface);
+}
+
+static int
+avro_resolved_record_reader_init(const avro_resolved_reader_t *iface, void *vself)
+{
+	int  rval;
+	const avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+	avro_resolved_record_value_t  *self = vself;
+
+	/* Initialize each field */
+	size_t  i;
+	for (i = 0; i < riface->field_count; i++) {
+		if (riface->field_resolvers[i] != NULL) {
+			check(rval, avro_resolved_reader_init
+			      (riface->field_resolvers[i],
+			       avro_resolved_record_field(riface, self, i)));
+		}
+	}
+
+	return 0;
+}
+
+static void
+avro_resolved_record_reader_done(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+	avro_resolved_record_value_t  *self = vself;
+
+	/* Finalize each field */
+	size_t  i;
+	for (i = 0; i < riface->field_count; i++) {
+		if (riface->field_resolvers[i] != NULL) {
+			avro_resolved_reader_done
+			    (riface->field_resolvers[i],
+			     avro_resolved_record_field(riface, self, i));
+		}
+	}
+}
+
+static int
+avro_resolved_record_reader_reset(const avro_resolved_reader_t *iface, void *vself)
+{
+	int  rval;
+	const avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+	avro_resolved_record_value_t  *self = vself;
+
+	/* Reset each field */
+	size_t  i;
+	for (i = 0; i < riface->field_count; i++) {
+		if (riface->field_resolvers[i] != NULL) {
+			check(rval, avro_resolved_reader_reset_wrappers
+			      (riface->field_resolvers[i],
+			       avro_resolved_record_field(riface, self, i)));
+		}
+	}
+
+	return 0;
+}
+
+static int
+avro_resolved_record_reader_get_size(const avro_value_iface_t *viface,
+				     const void *vself, size_t *size)
+{
+	AVRO_UNUSED(vself);
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	const avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+	*size = riface->field_count;
+	return 0;
+}
+
+static int
+avro_resolved_record_reader_get_by_index(const avro_value_iface_t *viface,
+					 const void *vself, size_t index,
+					 avro_value_t *child, const char **name)
+{
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	const avro_resolved_record_reader_t  *riface =
+	    container_of(iface, avro_resolved_record_reader_t, parent);
+	const avro_resolved_record_value_t  *self = vself;
+
+	DEBUG("Getting reader field %zu from record %p", index, self->wrapped.self);
+	if (riface->field_resolvers[index] == NULL) {
+		/*
+		 * TODO: Return the default value if the writer record
+		 * doesn't contain this field.
+		 */
+		DEBUG("Writer doesn't have field");
+		avro_set_error("NIY: Default values");
+		return EINVAL;
+	}
+
+	size_t  writer_index = riface->index_mapping[index];
+	DEBUG("  Writer field is %zu", writer_index);
+	child->iface = &riface->field_resolvers[index]->parent;
+	child->self = avro_resolved_record_field(riface, self, index);
+	return avro_value_get_by_index(&self->wrapped, writer_index, child->self, name);
+}
+
+static int
+avro_resolved_record_reader_get_by_name(const avro_value_iface_t *viface,
+					const void *vself, const char *name,
+					avro_value_t *child, size_t *index)
+{
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+
+	int  ri = avro_schema_record_field_get_index(iface->rschema, name);
+	if (ri == -1) {
+		avro_set_error("Record doesn't have field named %s", name);
+		return EINVAL;
+	}
+
+	DEBUG("Reader field %s is at index %d", name, ri);
+	if (index != NULL) {
+		*index = ri;
+	}
+	return avro_resolved_record_reader_get_by_index(viface, vself, ri, child, NULL);
+}
+
+static avro_resolved_record_reader_t *
+avro_resolved_record_reader_create(avro_schema_t wschema, avro_schema_t rschema)
+{
+	avro_resolved_reader_t  *self = avro_new(avro_resolved_record_reader_t);
+	memset(self, 0, sizeof(avro_resolved_record_reader_t));
+
+	self->parent.incref_iface = avro_resolved_reader_incref_iface;
+	self->parent.decref_iface = avro_resolved_reader_decref_iface;
+	self->parent.incref = avro_resolved_reader_incref;
+	self->parent.decref = avro_resolved_reader_decref;
+	self->parent.reset = avro_resolved_reader_reset;
+	self->parent.get_type = avro_resolved_reader_get_type;
+	self->parent.get_schema = avro_resolved_reader_get_schema;
+	self->parent.get_size = avro_resolved_record_reader_get_size;
+	self->parent.get_by_index = avro_resolved_record_reader_get_by_index;
+	self->parent.get_by_name = avro_resolved_record_reader_get_by_name;
+
+	self->refcount = 1;
+	self->wschema = avro_schema_incref(wschema);
+	self->rschema = avro_schema_incref(rschema);
+	self->calculate_size = avro_resolved_record_reader_calculate_size;
+	self->free_iface = avro_resolved_record_reader_free_iface;
+	self->init = avro_resolved_record_reader_init;
+	self->done = avro_resolved_record_reader_done;
+	self->reset_wrappers = avro_resolved_record_reader_reset;
+	return container_of(self, avro_resolved_record_reader_t, parent);
+}
+
+static avro_resolved_reader_t *
+try_record(memoize_state_t *state,
+	   avro_schema_t wschema, avro_schema_t rschema)
+{
+	/*
+	 * First verify that the writer is also a record, and has the
+	 * same name as the reader.
+	 */
+
+	if (!is_avro_record(wschema)) {
+		return 0;
+	}
+
+	const char  *wname = avro_schema_name(wschema);
+	const char  *rname = avro_schema_name(rschema);
+
+	if (strcmp(wname, rname) != 0) {
+		return 0;
+	}
+
+	/*
+	 * Categorize the fields in the record schemas.  Fields that are
+	 * only in the writer are ignored.  Fields that are only in the
+	 * reader raise a schema mismatch error, unless the field has a
+	 * default value.  Fields that are in both are resolved
+	 * recursively.
+	 *
+	 * The field_resolvers array will contain an avro_value_iface_t
+	 * for each field in the reader schema.  To build this array, we
+	 * loop through the fields of the reader schema.  If that field
+	 * is also in the writer schema, we resolve them recursively,
+	 * and store the resolver into the array.  If the field isn't in
+	 * the writer schema, we raise an error.  (TODO: Eventually,
+	 * we'll handle default values here.)  After this loop finishes,
+	 * any NULLs in the field_resolvers array will represent fields
+	 * in the writer but not the reader; these fields should be
+	 * skipped, and won't be accessible in the resolved reader.
+	 */
+
+	avro_resolved_record_reader_t  *rself =
+	    avro_resolved_record_reader_create(wschema, rschema);
+	avro_memoize_set(&state->mem, wschema, rschema, rself);
+
+	size_t  rfields = avro_schema_record_size(rschema);
+
+	DEBUG("Checking reader record schema %s", wname);
+
+	avro_resolved_reader_t  **field_resolvers =
+	    avro_calloc(rfields, sizeof(avro_resolved_reader_t *));
+	size_t  *field_offsets = avro_calloc(rfields, sizeof(size_t));
+	size_t  *index_mapping = avro_calloc(rfields, sizeof(size_t));
+
+	size_t  ri;
+	for (ri = 0; ri < rfields; ri++) {
+		avro_schema_t  rfield =
+		    avro_schema_record_field_get_by_index(rschema, ri);
+		const char  *field_name =
+		    avro_schema_record_field_name(rschema, ri);
+
+		DEBUG("Resolving reader record field %zu (%s)", ri, field_name);
+
+		/*
+		 * See if this field is also in the writer schema.
+		 */
+
+		int  wi = avro_schema_record_field_get_index(wschema, field_name);
+
+		if (wi == -1) {
+			/*
+			 * This field isn't in the writer schema —
+			 * that's an error!  TODO: Handle default
+			 * values!
+			 */
+
+			DEBUG("Field %s isn't in writer", field_name);
+			avro_set_error("Reader field %s doesn't appear in writer",
+				       field_name);
+			goto error;
+		}
+
+		/*
+		 * Try to recursively resolve the schemas for this
+		 * field.  If they're not compatible, that's an error.
+		 */
+
+		avro_schema_t  wfield =
+		    avro_schema_record_field_get_by_index(wschema, wi);
+		avro_resolved_reader_t  *field_resolver =
+		    avro_resolved_reader_new_memoized(state, wfield, rfield);
+
+		if (field_resolver == NULL) {
+			avro_prefix_error("Field %s isn't compatible: ", field_name);
+			goto error;
+		}
+
+		/*
+		 * Save the details for this field.
+		 */
+
+		DEBUG("Found match for field %s (%zu in reader, %d in writer)",
+		      field_name, ri, wi);
+		field_resolvers[ri] = field_resolver;
+		index_mapping[ri] = wi;
+	}
+
+	/*
+	 * We might not have found matches for all of the writer fields,
+	 * but that's okay — any extras will be ignored.
+	 */
+
+	rself->field_count = rfields;
+	rself->field_offsets = field_offsets;
+	rself->field_resolvers = field_resolvers;
+	rself->index_mapping = index_mapping;
+	return &rself->parent;
+
+error:
+	/*
+	 * Clean up any resolver we might have already created.
+	 */
+
+	avro_memoize_delete(&state->mem, wschema, rschema);
+	avro_value_iface_decref(&rself->parent.parent);
+
+	{
+		unsigned int  i;
+		for (i = 0; i < rfields; i++) {
+			if (field_resolvers[i]) {
+				avro_value_iface_decref(&field_resolvers[i]->parent);
+			}
+		}
+	}
+
+	avro_free(field_resolvers, rfields * sizeof(avro_resolved_reader_t *));
+	avro_free(field_offsets, rfields * sizeof(size_t));
+	avro_free(index_mapping, rfields * sizeof(size_t));
+	return NULL;
+}
+
+
+/*-----------------------------------------------------------------------
+ * writer union
+ */
+
+/*
+ * For writer unions, we maintain a list of resolvers for each branch of
+ * the union.  When we encounter a writer value, we see which branch it
+ * is, and choose a reader resolver based on that.
+ */
+
+typedef struct avro_resolved_wunion_reader {
+	avro_resolved_reader_t  parent;
+
+	/* The number of branches in the writer union */
+	size_t  branch_count;
+
+	/* A child resolver for each branch of the writer union.  If any
+	 * of these are NULL, then we don't have anything on the reader
+	 * side that's compatible with that writer branch. */
+	avro_resolved_reader_t  **branch_resolvers;
+
+} avro_resolved_wunion_reader_t;
+
+typedef struct avro_resolved_wunion_value {
+	avro_value_t  wrapped;
+
+	/** 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_resolved_wunion_value_t;
+
+/** Return a pointer to the active branch within a union struct. */
+#define avro_resolved_wunion_branch(_wunion) \
+	(((void *) (_wunion)) + sizeof(avro_resolved_wunion_value_t))
+
+
+static void
+avro_resolved_wunion_reader_calculate_size(avro_resolved_reader_t *iface)
+{
+	avro_resolved_wunion_reader_t  *uiface =
+	    container_of(iface, avro_resolved_wunion_reader_t, parent);
+
+	/* Only calculate the size for any resolver once */
+	iface->calculate_size = NULL;
+
+	DEBUG("Calculating size for %s->%s",
+	      avro_schema_type_name((iface)->wschema),
+	      avro_schema_type_name((iface)->rschema));
+
+	size_t  i;
+	size_t  max_branch_size = 0;
+	for (i = 0; i < uiface->branch_count; i++) {
+		if (uiface->branch_resolvers[i] == NULL) {
+			DEBUG("No match for writer union branch %zu", i);
+		} else {
+			avro_resolved_reader_calculate_size
+			    (uiface->branch_resolvers[i]);
+			size_t  branch_size =
+			    uiface->branch_resolvers[i]->instance_size;
+			DEBUG("Writer branch %zu has size %zu", i, branch_size);
+			if (branch_size > max_branch_size) {
+				max_branch_size = branch_size;
+			}
+		}
+	}
+
+	DEBUG("Maximum branch size is %zu", max_branch_size);
+	iface->instance_size =
+	    sizeof(avro_resolved_wunion_value_t) + max_branch_size;
+	DEBUG("Total union size is %zu", iface->instance_size);
+}
+
+
+static void
+avro_resolved_wunion_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing)
+{
+	avro_resolved_wunion_reader_t  *uiface =
+	    container_of(iface, avro_resolved_wunion_reader_t, parent);
+
+	if (uiface->branch_resolvers != NULL) {
+		size_t  i;
+		for (i = 0; i < uiface->branch_count; i++) {
+			if (uiface->branch_resolvers[i] != NULL) {
+				free_resolver(uiface->branch_resolvers[i], freeing);
+			}
+		}
+		avro_free(uiface->branch_resolvers,
+			  uiface->branch_count * sizeof(avro_resolved_reader_t *));
+	}
+
+	avro_schema_decref(iface->wschema);
+	avro_schema_decref(iface->rschema);
+	avro_freet(avro_resolved_wunion_reader_t, iface);
+}
+
+static int
+avro_resolved_wunion_reader_init(const avro_resolved_reader_t *iface, void *vself)
+{
+	AVRO_UNUSED(iface);
+	avro_resolved_wunion_value_t  *self = vself;
+	self->discriminant = -1;
+	return 0;
+}
+
+static void
+avro_resolved_wunion_reader_done(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_wunion_reader_t  *uiface =
+	    container_of(iface, avro_resolved_wunion_reader_t, parent);
+	avro_resolved_wunion_value_t  *self = vself;
+	if (self->discriminant >= 0) {
+		avro_resolved_reader_done
+		    (uiface->branch_resolvers[self->discriminant],
+		     avro_resolved_wunion_branch(self));
+		self->discriminant = -1;
+	}
+}
+
+static int
+avro_resolved_wunion_reader_reset(const avro_resolved_reader_t *iface, void *vself)
+{
+	const avro_resolved_wunion_reader_t  *uiface =
+	    container_of(iface, avro_resolved_wunion_reader_t, parent);
+	avro_resolved_wunion_value_t  *self = vself;
+
+	/* Keep the same branch selected, for the common case that we're
+	 * about to reuse it. */
+	if (self->discriminant >= 0) {
+		return avro_resolved_reader_reset_wrappers
+		    (uiface->branch_resolvers[self->discriminant],
+		     avro_resolved_wunion_branch(self));
+	}
+
+	return 0;
+}
+
+static int
+avro_resolved_wunion_get_real_src(const avro_value_iface_t *viface,
+				  const void *vself, avro_value_t *real_src)
+{
+	int  rval;
+	const avro_resolved_reader_t  *iface =
+	    container_of(viface, avro_resolved_reader_t, parent);
+	const avro_resolved_wunion_reader_t  *uiface =
+	    container_of(iface, avro_resolved_wunion_reader_t, parent);
+	avro_resolved_wunion_value_t  *self = (avro_resolved_wunion_value_t *) vself;
+	int  writer_disc;
+	check(rval, avro_value_get_discriminant(&self->wrapped, &writer_disc));
+	DEBUG("Writer is branch %d", writer_disc);
+
+	if (uiface->branch_resolvers[writer_disc] == NULL) {
+		avro_set_error("Reader isn't compatible with writer branch %d",
+			       writer_disc);
+		return EINVAL;
+	}
+
+	if (self->discriminant == writer_disc) {
+		DEBUG("Writer branch %d already selected", writer_disc);
+	} else {
+		if (self->discriminant >= 0) {
+			DEBUG("Finalizing old writer branch %d", self->discriminant);
+			avro_resolved_reader_done
+			    (uiface->branch_resolvers[self->discriminant],
+			     avro_resolved_wunion_branch(self));
+		}
+		DEBUG("Initializing writer branch %d", writer_disc);
+		check(rval, avro_resolved_reader_init
+		      (uiface->branch_resolvers[writer_disc],
+		       avro_resolved_wunion_branch(self)));
+		self->discriminant = writer_disc;
+	}
+
+	real_src->iface = &uiface->branch_resolvers[writer_disc]->parent;
+	real_src->self = avro_resolved_wunion_branch(self);
+	return avro_value_get_current_branch(&self->wrapped, real_src->self);
+}
+
+static int
+avro_resolved_wunion_reader_get_boolean(const avro_value_iface_t *viface,
+					const void *vself, int *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_boolean(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_bytes(const avro_value_iface_t *viface,
+				      const void *vself, const void **buf, size_t *size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_bytes(&src, buf, size);
+}
+
+static int
+avro_resolved_wunion_reader_grab_bytes(const avro_value_iface_t *viface,
+				       const void *vself, avro_wrapped_buffer_t *dest)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_grab_bytes(&src, dest);
+}
+
+static int
+avro_resolved_wunion_reader_get_double(const avro_value_iface_t *viface,
+				       const void *vself, double *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_double(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_float(const avro_value_iface_t *viface,
+				      const void *vself, float *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_float(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_int(const avro_value_iface_t *viface,
+				    const void *vself, int32_t *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_int(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_long(const avro_value_iface_t *viface,
+				     const void *vself, int64_t *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_long(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_null(const avro_value_iface_t *viface,
+				     const void *vself)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_null(&src);
+}
+
+static int
+avro_resolved_wunion_reader_get_string(const avro_value_iface_t *viface,
+				       const void *vself, const char **str, size_t *size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_string(&src, str, size);
+}
+
+static int
+avro_resolved_wunion_reader_grab_string(const avro_value_iface_t *viface,
+					const void *vself, avro_wrapped_buffer_t *dest)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_grab_string(&src, dest);
+}
+
+static int
+avro_resolved_wunion_reader_get_enum(const avro_value_iface_t *viface,
+				     const void *vself, int *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_enum(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_fixed(const avro_value_iface_t *viface,
+				      const void *vself, const void **buf, size_t *size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_fixed(&src, buf, size);
+}
+
+static int
+avro_resolved_wunion_reader_grab_fixed(const avro_value_iface_t *viface,
+				       const void *vself, avro_wrapped_buffer_t *dest)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_grab_fixed(&src, dest);
+}
+
+static int
+avro_resolved_wunion_reader_set_boolean(const avro_value_iface_t *viface,
+					void *vself, int val)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_boolean(&src, val);
+}
+
+static int
+avro_resolved_wunion_reader_set_bytes(const avro_value_iface_t *viface,
+				      void *vself, void *buf, size_t size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_bytes(&src, buf, size);
+}
+
+static int
+avro_resolved_wunion_reader_give_bytes(const avro_value_iface_t *viface,
+				       void *vself, avro_wrapped_buffer_t *buf)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_give_bytes(&src, buf);
+}
+
+static int
+avro_resolved_wunion_reader_set_double(const avro_value_iface_t *viface,
+				       void *vself, double val)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_double(&src, val);
+}
+
+static int
+avro_resolved_wunion_reader_set_float(const avro_value_iface_t *viface,
+				      void *vself, float val)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_float(&src, val);
+}
+
+static int
+avro_resolved_wunion_reader_set_int(const avro_value_iface_t *viface,
+				    void *vself, int32_t val)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_int(&src, val);
+}
+
+static int
+avro_resolved_wunion_reader_set_long(const avro_value_iface_t *viface,
+				     void *vself, int64_t val)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_long(&src, val);
+}
+
+static int
+avro_resolved_wunion_reader_set_null(const avro_value_iface_t *viface,
+				     void *vself)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_null(&src);
+}
+
+static int
+avro_resolved_wunion_reader_set_string(const avro_value_iface_t *viface,
+				       void *vself, char *str)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_string(&src, str);
+}
+
+static int
+avro_resolved_wunion_reader_set_string_len(const avro_value_iface_t *viface,
+					   void *vself, char *str, size_t size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_string_len(&src, str, size);
+}
+
+static int
+avro_resolved_wunion_reader_give_string_len(const avro_value_iface_t *viface,
+					    void *vself, avro_wrapped_buffer_t *buf)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_give_string_len(&src, buf);
+}
+
+static int
+avro_resolved_wunion_reader_set_enum(const avro_value_iface_t *viface,
+				     void *vself, int val)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_enum(&src, val);
+}
+
+static int
+avro_resolved_wunion_reader_set_fixed(const avro_value_iface_t *viface,
+				      void *vself, void *buf, size_t size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_set_fixed(&src, buf, size);
+}
+
+static int
+avro_resolved_wunion_reader_give_fixed(const avro_value_iface_t *viface,
+				       void *vself, avro_wrapped_buffer_t *dest)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_give_fixed(&src, dest);
+}
+
+static int
+avro_resolved_wunion_reader_get_size(const avro_value_iface_t *viface,
+				     const void *vself, size_t *size)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_size(&src, size);
+}
+
+static int
+avro_resolved_wunion_reader_get_by_index(const avro_value_iface_t *viface,
+					 const void *vself, size_t index,
+					 avro_value_t *child, const char **name)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_by_index(&src, index, child, name);
+}
+
+static int
+avro_resolved_wunion_reader_get_by_name(const avro_value_iface_t *viface,
+					const void *vself, const char *name,
+					avro_value_t *child, size_t *index)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_by_name(&src, name, child, index);
+}
+
+static int
+avro_resolved_wunion_reader_get_discriminant(const avro_value_iface_t *viface,
+					     const void *vself, int *out)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_discriminant(&src, out);
+}
+
+static int
+avro_resolved_wunion_reader_get_current_branch(const avro_value_iface_t *viface,
+					       const void *vself, avro_value_t *branch)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_get_current_branch(&src, branch);
+}
+
+static int
+avro_resolved_wunion_reader_append(const avro_value_iface_t *viface,
+				   void *vself, avro_value_t *child_out,
+				   size_t *new_index)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));
+	return avro_value_append(&src, child_out, new_index);
+}
+
+static int
+avro_resolved_wunion_reader_add(const avro_value_iface_t *viface,
+				void *vself, const char *key,
+				avro_value_t *child, size_t *index, int *is_new)
+{
+	int  rval;
+	avro_value_t  src;
+	check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src));

[... 516 lines stripped ...]


Mime
View raw message