static int
avro_datum_value_get_size(const avro_value_iface_t *iface,
			  const void *vself, size_t *size)
{
	AVRO_UNUSED(iface);
	const avro_datum_t  self = (const avro_datum_t) vself;
	check_param(EINVAL, self, "datum instance");

	if (is_avro_array(self)) {
		*size = avro_array_size(self);
		return 0;
	}

	if (is_avro_map(self)) {
		*size = avro_map_size(self);
		return 0;
	}

	if (is_avro_record(self)) {
		avro_schema_t  schema = avro_datum_get_schema(self);
		*size = avro_schema_record_size(schema);
		return 0;
	}

	avro_set_error("Can only get size of array, map, or record, %d", avro_typeof(self));
	return EINVAL;
}
Beispiel #2
0
static int
l_schema_type(lua_State *L)
{
	avro_schema_t  schema = lua_avro_get_raw_schema(L, 1);
	lua_pushnumber(L, avro_typeof(schema));
	return 1;
}
Beispiel #3
0
int avro_schema_to_json(const avro_schema_t schema, avro_writer_t out)
{
	check_param(EINVAL, is_avro_schema(schema), "schema");
	check_param(EINVAL, out, "writer");

	int rval;

	if (is_avro_primitive(schema)) {
		check(rval, avro_write_str(out, "{\"type\":\""));
	}

	switch (avro_typeof(schema)) {
	case AVRO_STRING:
		check(rval, avro_write_str(out, "string"));
		break;
	case AVRO_BYTES:
		check(rval, avro_write_str(out, "bytes"));
		break;
	case AVRO_INT32:
		check(rval, avro_write_str(out, "int"));
		break;
	case AVRO_INT64:
		check(rval, avro_write_str(out, "long"));
		break;
	case AVRO_FLOAT:
		check(rval, avro_write_str(out, "float"));
		break;
	case AVRO_DOUBLE:
		check(rval, avro_write_str(out, "double"));
		break;
	case AVRO_BOOLEAN:
		check(rval, avro_write_str(out, "boolean"));
		break;
	case AVRO_NULL:
		check(rval, avro_write_str(out, "null"));
		break;
	case AVRO_RECORD:
		return write_record(out, avro_schema_to_record(schema));
	case AVRO_ENUM:
		return write_enum(out, avro_schema_to_enum(schema));
	case AVRO_FIXED:
		return write_fixed(out, avro_schema_to_fixed(schema));
	case AVRO_MAP:
		return write_map(out, avro_schema_to_map(schema));
	case AVRO_ARRAY:
		return write_array(out, avro_schema_to_array(schema));
	case AVRO_UNION:
		return write_union(out, avro_schema_to_union(schema));
	case AVRO_LINK:
		return write_link(out, avro_schema_to_link(schema));
	}

	if (is_avro_primitive(schema)) {
		return avro_write_str(out, "\"}");
	}
	avro_set_error("Unknown schema type");
	return EINVAL;
}
Beispiel #4
0
int avro_schema_equal(avro_schema_t a, avro_schema_t b)
{
	if (!a || !b) {
		/*
		 * this is an error. protecting from segfault. 
		 */
		return 0;
	} else if (a == b) {
		/*
		 * an object is equal to itself 
		 */
		return 1;
	} else if (avro_typeof(a) != avro_typeof(b)) {
		return 0;
	} else if (is_avro_record(a)) {
		return schema_record_equal(avro_schema_to_record(a),
					   avro_schema_to_record(b));
	} else if (is_avro_enum(a)) {
		return schema_enum_equal(avro_schema_to_enum(a),
					 avro_schema_to_enum(b));
	} else if (is_avro_fixed(a)) {
		return schema_fixed_equal(avro_schema_to_fixed(a),
					  avro_schema_to_fixed(b));
	} else if (is_avro_map(a)) {
		return schema_map_equal(avro_schema_to_map(a),
					avro_schema_to_map(b));
	} else if (is_avro_array(a)) {
		return schema_array_equal(avro_schema_to_array(a),
					  avro_schema_to_array(b));
	} else if (is_avro_union(a)) {
		return schema_union_equal(avro_schema_to_union(a),
					  avro_schema_to_union(b));
	} else if (is_avro_link(a)) {
		return schema_link_equal(avro_schema_to_link(a),
					 avro_schema_to_link(b));
	}
	return 1;
}
static avro_type_t
avro_datum_value_get_type(const avro_value_iface_t *iface, const void *vself)
{
	AVRO_UNUSED(iface);
	const avro_datum_t  self = (const avro_datum_t) vself;
#ifdef _WIN32
#pragma message("#warning: Bug: EINVAL is not of type avro_type_t.")
#else
//#warning "Bug: EINVAL is not of type avro_type_t."
#endif
        /* We shouldn't use EINVAL as the return value to
         * check_param(), because EINVAL (= 22) is not a valid enum
         * avro_type_t. This is a structural issue -- we would need a
         * different interface on all the get_type functions to fix
         * this. For now, suppressing the error by casting EINVAL to
         * (avro_type_t) so the code compiles under C++.
         */
	check_param((avro_type_t) EINVAL, self, "datum instance");
	return avro_typeof(self);
}
avro_schema_t r_kv_get_schema(kv_store_t *kvstore,
                             const char *space,
                             const char *name) {
    int nsch, i;
    avro_schema_t *schemas = NULL, schema = NULL;

    if (!kvstore || !name) {
        return NULL;
    }

    schema = avro_schema_record(name, space);
    nsch = kv_avro_get_current_schemas(kvstore, &schemas);
    PRINTF("r_kv_get_schema, name = %s, space = %s, nsch = %d\n", name, space, nsch);
    for (i = 0; i < nsch; i++) {
        avro_schema_t sch = schemas[i];
        if (avro_typeof(sch) == AVRO_RECORD &&
            avro_schema_equal(schema, sch)) {
            avro_schema_decref(schema);
            return sch;
        }
    }
    avro_schema_decref(schema);
    return NULL;
}
Beispiel #7
0
static void avro_schema_free(avro_schema_t schema)
{
	if (is_avro_schema(schema)) {
		switch (avro_typeof(schema)) {
		case AVRO_STRING:
		case AVRO_BYTES:
		case AVRO_INT32:
		case AVRO_INT64:
		case AVRO_FLOAT:
		case AVRO_DOUBLE:
		case AVRO_BOOLEAN:
		case AVRO_NULL:
			/* no memory allocated for primitives */
			return;

		case AVRO_RECORD:{
				struct avro_record_schema_t *record;
				record = avro_schema_to_record(schema);
				avro_str_free(record->name);
				if (record->space) {
					avro_str_free(record->space);
				}
				st_foreach(record->fields, HASH_FUNCTION_CAST record_free_foreach,
					   0);
				st_free_table(record->fields_byname);
				st_free_table(record->fields);
				avro_freet(struct avro_record_schema_t, record);
			}
			break;

		case AVRO_ENUM:{
				struct avro_enum_schema_t *enump;
				enump = avro_schema_to_enum(schema);
				avro_str_free(enump->name);
				if (enump->space) {
					avro_str_free(enump->space);
				}
				st_foreach(enump->symbols, HASH_FUNCTION_CAST enum_free_foreach,
					   0);
				st_free_table(enump->symbols);
				st_free_table(enump->symbols_byname);
				avro_freet(struct avro_enum_schema_t, enump);
			}
			break;

		case AVRO_FIXED:{
				struct avro_fixed_schema_t *fixed;
				fixed = avro_schema_to_fixed(schema);
				avro_str_free((char *) fixed->name);
				if (fixed->space) {
					avro_str_free((char *) fixed->space);
				}
				avro_freet(struct avro_fixed_schema_t, fixed);
			}
			break;

		case AVRO_MAP:{
				struct avro_map_schema_t *map;
				map = avro_schema_to_map(schema);
				avro_schema_decref(map->values);
				avro_freet(struct avro_map_schema_t, map);
			}
			break;

		case AVRO_ARRAY:{
				struct avro_array_schema_t *array;
				array = avro_schema_to_array(schema);
				avro_schema_decref(array->items);
				avro_freet(struct avro_array_schema_t, array);
			}
			break;
		case AVRO_UNION:{
				struct avro_union_schema_t *unionp;
				unionp = avro_schema_to_union(schema);
				st_foreach(unionp->branches, HASH_FUNCTION_CAST union_free_foreach,
					   0);
				st_free_table(unionp->branches);
				st_free_table(unionp->branches_byname);
				avro_freet(struct avro_union_schema_t, unionp);
			}
			break;

		case AVRO_LINK:{
				struct avro_link_schema_t *link;
				link = avro_schema_to_link(schema);
				avro_schema_decref(link->to);
				avro_freet(struct avro_link_schema_t, link);
			}
			break;
		}
	}
}
Beispiel #8
0
avro_datum_t avro_datum_from_schema(const avro_schema_t schema)
{
	check_param(NULL, is_avro_schema(schema), "schema");

	switch (avro_typeof(schema)) {
		case AVRO_STRING:
			return avro_givestring("", NULL);

		case AVRO_BYTES:
			return avro_givebytes("", 0, NULL);

		case AVRO_INT32:
			return avro_int32(0);

		case AVRO_INT64:
			return avro_int64(0);

		case AVRO_FLOAT:
			return avro_float(0);

		case AVRO_DOUBLE:
			return avro_double(0);

		case AVRO_BOOLEAN:
			return avro_boolean(0);

		case AVRO_NULL:
			return avro_null();

		case AVRO_RECORD:
			{
				const struct avro_record_schema_t *record_schema =
				    avro_schema_to_record(schema);

				avro_datum_t  rec = avro_record(schema);

				int  i;
				for (i = 0; i < record_schema->fields->num_entries; i++) {
					union {
						st_data_t data;
						struct avro_record_field_t *field;
					} val;
					st_lookup(record_schema->fields, i, &val.data);

					avro_datum_t  field =
					    avro_datum_from_schema(val.field->type);
					avro_record_set(rec, val.field->name, field);
					avro_datum_decref(field);
				}

				return rec;
			}

		case AVRO_ENUM:
			return avro_enum(schema, 0);

		case AVRO_FIXED:
			{
				const struct avro_fixed_schema_t *fixed_schema =
				    avro_schema_to_fixed(schema);
				return avro_givefixed(schema, NULL, fixed_schema->size, NULL);
			}

		case AVRO_MAP:
			return avro_map(schema);

		case AVRO_ARRAY:
			return avro_array(schema);

		case AVRO_UNION:
			return avro_union(schema, -1, NULL);

		case AVRO_LINK:
			{
				const struct avro_link_schema_t *link_schema =
				    avro_schema_to_link(schema);
				return avro_datum_from_schema(link_schema->to);
			}

		default:
			avro_set_error("Unknown schema type");
			return NULL;
	}
}
Beispiel #9
0
avro_schema_t avro_schema_copy(avro_schema_t schema)
{
	long i;
	avro_schema_t new_schema = NULL;
	if (!schema) {
		return NULL;
	}
	switch (avro_typeof(schema)) {
	case AVRO_STRING:
	case AVRO_BYTES:
	case AVRO_INT32:
	case AVRO_INT64:
	case AVRO_FLOAT:
	case AVRO_DOUBLE:
	case AVRO_BOOLEAN:
	case AVRO_NULL:
		/*
		 * No need to copy primitives since they're static 
		 */
		new_schema = schema;
		break;

	case AVRO_RECORD:
		{
			struct avro_record_schema_t *record_schema =
			    avro_schema_to_record(schema);
			new_schema =
			    avro_schema_record(record_schema->name,
					       record_schema->space);
			for (i = 0; i < record_schema->fields->num_entries; i++) {
				union {
					st_data_t data;
					struct avro_record_field_t *field;
				} val;
				st_lookup(record_schema->fields, i, &val.data);
				avro_schema_t type_copy =
				    avro_schema_copy(val.field->type);
				avro_datum_incref(val.field->default_value);
				avro_schema_record_field_append(new_schema,
								val.field->name,
								type_copy,
								val.field->default_value);
			}
		}
		break;

	case AVRO_ENUM:
		{
			struct avro_enum_schema_t *enum_schema =
			    avro_schema_to_enum(schema);
			new_schema = avro_schema_enum(enum_schema->name);
			for (i = 0; i < enum_schema->symbols->num_entries; i++) {
				union {
					st_data_t data;
					char *sym;
				} val;
				st_lookup(enum_schema->symbols, i, &val.data);
				avro_schema_enum_symbol_append(new_schema,
							       val.sym);
			}
		}
		break;

	case AVRO_FIXED:
		{
			struct avro_fixed_schema_t *fixed_schema =
			    avro_schema_to_fixed(schema);
			new_schema =
			    avro_schema_fixed(fixed_schema->name,
					      fixed_schema->size);
		}
		break;

	case AVRO_MAP:
		{
			struct avro_map_schema_t *map_schema =
			    avro_schema_to_map(schema);
			avro_schema_t values_copy =
			    avro_schema_copy(map_schema->values);
			if (!values_copy) {
				return NULL;
			}
			new_schema = avro_schema_map(values_copy);
		}
		break;

	case AVRO_ARRAY:
		{
			struct avro_array_schema_t *array_schema =
			    avro_schema_to_array(schema);
			avro_schema_t items_copy =
			    avro_schema_copy(array_schema->items);
			if (!items_copy) {
				return NULL;
			}
			new_schema = avro_schema_array(items_copy);
		}
		break;

	case AVRO_UNION:
		{
			struct avro_union_schema_t *union_schema =
			    avro_schema_to_union(schema);

			new_schema = avro_schema_union();
			for (i = 0; i < union_schema->branches->num_entries;
			     i++) {
				avro_schema_t schema_copy;
				union {
					st_data_t data;
					avro_schema_t schema;
				} val;
				st_lookup(union_schema->branches, i, &val.data);
				schema_copy = avro_schema_copy(val.schema);
				if (avro_schema_union_append
				    (new_schema, schema_copy)) {
					avro_schema_decref(new_schema);
					return NULL;
				}
			}
		}
		break;

	case AVRO_LINK:
		{
			struct avro_link_schema_t *link_schema =
			    avro_schema_to_link(schema);
			/*
			 * TODO: use an avro_schema_copy of to instead of pointing to
			 * the same reference 
			 */
			avro_schema_incref(link_schema->to);
			new_schema = avro_schema_link(link_schema->to);
		}
		break;

	default:
		return NULL;
	}
	return new_schema;
}
Beispiel #10
0
int avro_datum_equal(const avro_datum_t a, const avro_datum_t b)
{
	if (!(is_avro_datum(a) && is_avro_datum(b))) {
		return 0;
	}
	if (avro_typeof(a) != avro_typeof(b)) {
		return 0;
	}
	switch (avro_typeof(a)) {
	case AVRO_STRING:
		return strcmp(avro_datum_to_string(a)->s,
			      avro_datum_to_string(b)->s) == 0;
	case AVRO_BYTES:
		return (avro_datum_to_bytes(a)->size ==
			avro_datum_to_bytes(b)->size)
		    && memcmp(avro_datum_to_bytes(a)->bytes,
			      avro_datum_to_bytes(b)->bytes,
			      avro_datum_to_bytes(a)->size) == 0;
	case AVRO_INT32:
		return avro_datum_to_int32(a)->i32 ==
		    avro_datum_to_int32(b)->i32;
	case AVRO_INT64:
		return avro_datum_to_int64(a)->i64 ==
		    avro_datum_to_int64(b)->i64;
	case AVRO_FLOAT:
		return avro_datum_to_float(a)->f == avro_datum_to_float(b)->f;
	case AVRO_DOUBLE:
		return avro_datum_to_double(a)->d == avro_datum_to_double(b)->d;
	case AVRO_BOOLEAN:
		return avro_datum_to_boolean(a)->i ==
		    avro_datum_to_boolean(b)->i;
	case AVRO_NULL:
		return 1;
	case AVRO_ARRAY:
		return array_equal(avro_datum_to_array(a),
				   avro_datum_to_array(b));
	case AVRO_MAP:
		return map_equal(avro_datum_to_map(a), avro_datum_to_map(b));

	case AVRO_RECORD:
		return record_equal(avro_datum_to_record(a),
				    avro_datum_to_record(b));

	case AVRO_ENUM:
		return enum_equal(avro_datum_to_enum(a), avro_datum_to_enum(b));

	case AVRO_FIXED:
		return fixed_equal(avro_datum_to_fixed(a),
				   avro_datum_to_fixed(b));

	case AVRO_UNION:
		return union_equal(avro_datum_to_union(a),
				   avro_datum_to_union(b));

	case AVRO_LINK:
		/*
		 * TODO 
		 */
		return 0;
	}
	return 0;
}
int
avro_consume_binary(avro_reader_t reader, avro_consumer_t *consumer, void *ud)
{
	int rval;
	const avro_encoding_t *enc = &avro_binary_encoding;

	check_param(EINVAL, reader, "reader");
	check_param(EINVAL, consumer, "consumer");

	switch (avro_typeof(consumer->schema)) {
	case AVRO_NULL:
		check_prefix(rval, enc->read_null(reader),
			     "Cannot read null value: ");
		check(rval, avro_consumer_call(consumer, null_value, ud));
		break;

	case AVRO_BOOLEAN:
		{
			int8_t b;
			check_prefix(rval, enc->read_boolean(reader, &b),
				     "Cannot read boolean value: ");
			check(rval, avro_consumer_call(consumer, boolean_value, b, ud));
		}
		break;

	case AVRO_STRING:
		{
			int64_t len;
			char *s;
			check_prefix(rval, enc->read_string(reader, &s, &len),
				     "Cannot read string value: ");
			check(rval, avro_consumer_call(consumer, string_value, s, len, ud));
		}
		break;

	case AVRO_INT32:
		{
			int32_t i;
			check_prefix(rval, enc->read_int(reader, &i),
				    "Cannot read int value: ");
			check(rval, avro_consumer_call(consumer, int_value, i, ud));
		}
		break;

	case AVRO_INT64:
		{
			int64_t l;
			check_prefix(rval, enc->read_long(reader, &l),
				     "Cannot read long value: ");
			check(rval, avro_consumer_call(consumer, long_value, l, ud));
		}
		break;

	case AVRO_FLOAT:
		{
			float f;
			check_prefix(rval, enc->read_float(reader, &f),
				     "Cannot read float value: ");
			check(rval, avro_consumer_call(consumer, float_value, f, ud));
		}
		break;

	case AVRO_DOUBLE:
		{
			double d;
			check_prefix(rval, enc->read_double(reader, &d),
				     "Cannot read double value: ");
			check(rval, avro_consumer_call(consumer, double_value, d, ud));
		}
		break;

	case AVRO_BYTES:
		{
			char *bytes;
			int64_t len;
			check_prefix(rval, enc->read_bytes(reader, &bytes, &len),
				     "Cannot read bytes value: ");
			check(rval, avro_consumer_call(consumer, bytes_value, bytes, len, ud));
		}
		break;

	case AVRO_FIXED:
		{
			char *bytes;
			int64_t size =
			    avro_schema_to_fixed(consumer->schema)->size;

			bytes = avro_malloc(size);
			if (!bytes) {
				avro_prefix_error("Cannot allocate new fixed value");
				return ENOMEM;
			}
			rval = avro_read(reader, bytes, size);
			if (rval) {
				avro_prefix_error("Cannot read fixed value: ");
				avro_free(bytes, size);
				return rval;
			}

			rval = avro_consumer_call(consumer, fixed_value, bytes, size, ud);
			if (rval) {
				avro_free(bytes, size);
				return rval;
			}
		}
		break;

	case AVRO_ENUM:
		check(rval, read_enum(reader, enc, consumer, ud));
		break;

	case AVRO_ARRAY:
		check(rval, read_array(reader, enc, consumer, ud));
		break;

	case AVRO_MAP:
		check(rval, read_map(reader, enc, consumer, ud));
		break;

	case AVRO_UNION:
		check(rval, read_union(reader, enc, consumer, ud));
		break;

	case AVRO_RECORD:
		check(rval, read_record(reader, enc, consumer, ud));
		break;

	case AVRO_LINK:
		avro_set_error("Consumer can't consume a link schema directly");
		return EINVAL;
	}

	return 0;
}
Beispiel #12
0
static int write_datum(avro_writer_t writer, const avro_encoding_t * enc,
		       avro_schema_t writers_schema, avro_datum_t datum)
{
	if (is_avro_schema(writers_schema) && is_avro_link(writers_schema)) {
		return write_datum(writer, enc,
				   (avro_schema_to_link(writers_schema))->to,
				   datum);
	}

	switch (avro_typeof(datum)) {
	case AVRO_NULL:
		return enc->write_null(writer);

	case AVRO_BOOLEAN:
		return enc->write_boolean(writer,
					  avro_datum_to_boolean(datum)->i);

	case AVRO_STRING:
		return enc->write_string(writer,
					 avro_datum_to_string(datum)->s);

	case AVRO_BYTES:
		return enc->write_bytes(writer,
					avro_datum_to_bytes(datum)->bytes,
					avro_datum_to_bytes(datum)->size);

	case AVRO_INT32:
	case AVRO_INT64:{
			int64_t val = avro_typeof(datum) == AVRO_INT32 ?
			    avro_datum_to_int32(datum)->i32 :
			    avro_datum_to_int64(datum)->i64;
			if (is_avro_schema(writers_schema)) {
				/* handle promotion */
				if (is_avro_float(writers_schema)) {
					return enc->write_float(writer,
								(float)val);
				} else if (is_avro_double(writers_schema)) {
					return enc->write_double(writer,
								 (double)val);
				}
			}
			return enc->write_long(writer, val);
		}

	case AVRO_FLOAT:{
			float val = avro_datum_to_float(datum)->f;
			if (is_avro_schema(writers_schema)
			    && is_avro_double(writers_schema)) {
				/* handle promotion */
				return enc->write_double(writer, (double)val);
			}
			return enc->write_float(writer, val);
		}

	case AVRO_DOUBLE:
		return enc->write_double(writer,
					 avro_datum_to_double(datum)->d);

	case AVRO_RECORD:
		return write_record(writer, enc,
				    avro_schema_to_record(writers_schema),
				    datum);

	case AVRO_ENUM:
		return write_enum(writer, enc,
				  avro_schema_to_enum(writers_schema),
				  avro_datum_to_enum(datum));

	case AVRO_FIXED:
		return avro_write(writer,
				  avro_datum_to_fixed(datum)->bytes,
				  avro_datum_to_fixed(datum)->size);

	case AVRO_MAP:
		return write_map(writer, enc,
				 avro_schema_to_map(writers_schema),
				 avro_datum_to_map(datum));

	case AVRO_ARRAY:
		return write_array(writer, enc,
				   avro_schema_to_array(writers_schema),
				   avro_datum_to_array(datum));

	case AVRO_UNION:
		return write_union(writer, enc,
				   avro_schema_to_union(writers_schema),
				   avro_datum_to_union(datum));

	case AVRO_LINK:
		break;
	}

	return 0;
}
Beispiel #13
0
avro_schema_t avro_schema_string(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_STRING,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_bytes(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_BYTES,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_int(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_INT32,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_long(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_INT64,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_float(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_FLOAT,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_double(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_DOUBLE,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_boolean(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_BOOLEAN,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_null(void)
{
	static struct avro_obj_t obj = {
		.type = AVRO_NULL,
		.class_type = AVRO_SCHEMA,
		.refcount = 1
	};
	return &obj;
}

avro_schema_t avro_schema_fixed(const char *name, const int64_t size)
{
	struct avro_fixed_schema_t *fixed =
	    malloc(sizeof(struct avro_fixed_schema_t));
	if (!fixed) {
		return NULL;
	}
	if (!is_avro_id(name)) {
		return NULL;
	}
	fixed->name = strdup(name);
	fixed->size = size;
	avro_schema_init(&fixed->obj, AVRO_FIXED);
	return &fixed->obj;
}

avro_schema_t avro_schema_union(void)
{
	struct avro_union_schema_t *schema =
	    malloc(sizeof(struct avro_union_schema_t));
	if (!schema) {
		return NULL;
	}
	schema->branches = st_init_numtable_with_size(DEFAULT_TABLE_SIZE);
	if (!schema->branches) {
		free(schema);
		return NULL;
	}

	avro_schema_init(&schema->obj, AVRO_UNION);
	return &schema->obj;
}

int
avro_schema_union_append(const avro_schema_t union_schema,
			 const avro_schema_t schema)
{
	struct avro_union_schema_t *unionp;
	if (!union_schema || !schema || !is_avro_union(union_schema)) {
		return EINVAL;
	}
	unionp = avro_schema_to_union(union_schema);
	st_insert(unionp->branches, unionp->branches->num_entries,
		  (st_data_t) schema);
	avro_schema_incref(schema);
	return 0;
}

avro_schema_t avro_schema_array(const avro_schema_t items)
{
	struct avro_array_schema_t *array =
	    malloc(sizeof(struct avro_array_schema_t));
	if (!array) {
		return NULL;
	}
	array->items = avro_schema_incref(items);
	avro_schema_init(&array->obj, AVRO_ARRAY);
	return &array->obj;
}

avro_schema_t avro_schema_map(const avro_schema_t values)
{
	struct avro_map_schema_t *map =
	    malloc(sizeof(struct avro_map_schema_t));
	if (!map) {
		return NULL;
	}
	map->values = avro_schema_incref(values);
	avro_schema_init(&map->obj, AVRO_MAP);
	return &map->obj;
}

avro_schema_t avro_schema_enum(const char *name)
{
	struct avro_enum_schema_t *enump;

	if (!is_avro_id(name)) {
		return NULL;
	}
	enump = malloc(sizeof(struct avro_enum_schema_t));
	if (!enump) {
		return NULL;
	}
	enump->name = strdup(name);
	if (!enump->name) {
		free(enump);
		return NULL;
	}
	enump->symbols = st_init_numtable_with_size(DEFAULT_TABLE_SIZE);
	if (!enump->symbols) {
		free(enump->name);
		free(enump);
		return NULL;
	}
	enump->symbols_byname = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
	if (!enump->symbols_byname) {
		st_free_table(enump->symbols);
		free(enump->name);
		free(enump);
		return NULL;
	}
	avro_schema_init(&enump->obj, AVRO_ENUM);
	return &enump->obj;
}

int
avro_schema_enum_symbol_append(const avro_schema_t enum_schema,
			       const char *symbol)
{
	struct avro_enum_schema_t *enump;
	char *sym;
	long idx;
	if (!enum_schema || !symbol || !is_avro_enum(enum_schema)) {
		return EINVAL;
	}
	enump = avro_schema_to_enum(enum_schema);
	sym = strdup(symbol);
	if (!sym) {
		return ENOMEM;
	}
	idx = enump->symbols->num_entries;
	st_insert(enump->symbols, (st_data_t) idx, (st_data_t) sym);
	st_insert(enump->symbols_byname, (st_data_t) sym, (st_data_t) idx);
	return 0;
}

int
avro_schema_record_field_append(const avro_schema_t record_schema,
				const char *field_name,
				const avro_schema_t field_schema)
{
	struct avro_record_schema_t *record;
	struct avro_record_field_t *new_field;
	if (!field_name || !field_schema || !is_avro_schema(record_schema)
	    || !is_avro_record(record_schema) || record_schema == field_schema
	    || !is_avro_id(field_name)) {
		return EINVAL;
	}
	record = avro_schema_to_record(record_schema);
	new_field = malloc(sizeof(struct avro_record_field_t));
	if (!new_field) {
		return ENOMEM;
	}
	new_field->name = strdup(field_name);
	new_field->type = avro_schema_incref(field_schema);
	st_insert(record->fields, record->fields->num_entries,
		  (st_data_t) new_field);
	st_insert(record->fields_byname, (st_data_t) new_field->name,
		  (st_data_t) new_field);
	return 0;
}

avro_schema_t avro_schema_record(const char *name, const char *space)
{
	struct avro_record_schema_t *record;
	if (!is_avro_id(name)) {
		return NULL;
	}
	record = malloc(sizeof(struct avro_record_schema_t));
	if (!record) {
		return NULL;
	}
	record->name = strdup(name);
	if (!record->name) {
		free(record);
		return NULL;
	}
	record->space = space ? strdup(space) : NULL;
	if (space && !record->space) {
		free(record->name);
		free(record);
		return NULL;
	}
	record->fields = st_init_numtable_with_size(DEFAULT_TABLE_SIZE);
	if (!record->fields) {
		if (record->space) {
			free(record->space);
		}
		free(record->name);
		free(record);
		return NULL;
	}
	record->fields_byname = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
	if (!record->fields_byname) {
		st_free_table(record->fields);
		free(record->name);
		free(record);
		return NULL;
	}

	avro_schema_init(&record->obj, AVRO_RECORD);
	return &record->obj;
}

static int
save_named_schemas(const char *name, avro_schema_t schema,
		   avro_schema_error_t * error)
{
	st_table *st = (*error)->named_schemas;
	return st_insert(st, (st_data_t) name, (st_data_t) schema);
}

static avro_schema_t
find_named_schemas(const char *name, avro_schema_error_t * error)
{
	st_table *st = (*error)->named_schemas;
	union {
		avro_schema_t schema;
		st_data_t data;
	} val;
	if (st_lookup(st, (st_data_t) name, &(val.data))) {
		return val.schema;
	}
	return NULL;
};

avro_schema_t avro_schema_link(avro_schema_t to)
{
	struct avro_link_schema_t *link;
	if (!is_avro_named_type(to)) {
		return NULL;
	}
	link = malloc(sizeof(struct avro_link_schema_t));
	if (!link) {
		return NULL;
	}
	link->to = avro_schema_incref(to);
	avro_schema_init(&link->obj, AVRO_LINK);
	return &link->obj;
}

static int
avro_type_from_json_t(json_t * json, avro_type_t * type,
		      avro_schema_error_t * error, avro_schema_t * named_type)
{
	json_t *json_type;
	const char *type_str;

	if (json_is_array(json)) {
		*type = AVRO_UNION;
		return 0;
	} else if (json_is_object(json)) {
		json_type = json_object_get(json, "type");
	} else {
		json_type = json;
	}
	if (!json_is_string(json_type)) {
		return EINVAL;
	}
	type_str = json_string_value(json_type);
	if (!type_str) {
		return EINVAL;
	}
	/*
	 * TODO: gperf/re2c this 
	 */
	if (strcmp(type_str, "string") == 0) {
		*type = AVRO_STRING;
	} else if (strcmp(type_str, "bytes") == 0) {
		*type = AVRO_BYTES;
	} else if (strcmp(type_str, "int") == 0) {
		*type = AVRO_INT32;
	} else if (strcmp(type_str, "long") == 0) {
		*type = AVRO_INT64;
	} else if (strcmp(type_str, "float") == 0) {
		*type = AVRO_FLOAT;
	} else if (strcmp(type_str, "double") == 0) {
		*type = AVRO_DOUBLE;
	} else if (strcmp(type_str, "boolean") == 0) {
		*type = AVRO_BOOLEAN;
	} else if (strcmp(type_str, "null") == 0) {
		*type = AVRO_NULL;
	} else if (strcmp(type_str, "record") == 0) {
		*type = AVRO_RECORD;
	} else if (strcmp(type_str, "enum") == 0) {
		*type = AVRO_ENUM;
	} else if (strcmp(type_str, "array") == 0) {
		*type = AVRO_ARRAY;
	} else if (strcmp(type_str, "map") == 0) {
		*type = AVRO_MAP;
	} else if (strcmp(type_str, "fixed") == 0) {
		*type = AVRO_FIXED;
	} else if ((*named_type = find_named_schemas(type_str, error))) {
		*type = AVRO_LINK;
	} else {
		return EINVAL;
	}
	return 0;
}

static int
avro_schema_from_json_t(json_t * json, avro_schema_t * schema,
			avro_schema_error_t * error)
{
	avro_type_t type = 0;
	unsigned int i;
	avro_schema_t named_schemas = NULL;

	if (avro_type_from_json_t(json, &type, error, &named_schemas)) {
		return EINVAL;
	}

	switch (type) {
	case AVRO_LINK:
		*schema = avro_schema_link(named_schemas);
		break;

	case AVRO_STRING:
		*schema = avro_schema_string();
		break;

	case AVRO_BYTES:
		*schema = avro_schema_bytes();
		break;

	case AVRO_INT32:
		*schema = avro_schema_int();
		break;

	case AVRO_INT64:
		*schema = avro_schema_long();
		break;

	case AVRO_FLOAT:
		*schema = avro_schema_float();
		break;

	case AVRO_DOUBLE:
		*schema = avro_schema_double();
		break;

	case AVRO_BOOLEAN:
		*schema = avro_schema_boolean();
		break;

	case AVRO_NULL:
		*schema = avro_schema_null();
		break;

	case AVRO_RECORD:
		{
			json_t *json_name = json_object_get(json, "name");
			json_t *json_namespace =
			    json_object_get(json, "namespace");
			json_t *json_fields = json_object_get(json, "fields");
			unsigned int num_fields;
			const char *record_name;
			const char *record_namespace;

			if (!json_is_string(json_name)) {
				return EINVAL;
			}
			if (!json_is_array(json_fields)) {
				return EINVAL;
			}
			num_fields = json_array_size(json_fields);
			if (num_fields == 0) {
				return EINVAL;
			}
			record_name = json_string_value(json_name);
			if (!record_name) {
				return EINVAL;
			}
			if (json_is_string(json_namespace)) {
				record_namespace =
				    json_string_value(json_namespace);
			} else {
				record_namespace = NULL;
			}
			*schema =
			    avro_schema_record(record_name, record_namespace);
			if (save_named_schemas(record_name, *schema, error)) {
				return ENOMEM;
			}
			for (i = 0; i < num_fields; i++) {
				json_t *json_field =
				    json_array_get(json_fields, i);
				json_t *json_field_name;
				json_t *json_field_type;
				avro_schema_t json_field_type_schema;
				int field_rval;

				if (!json_is_object(json_field)) {
					avro_schema_decref(*schema);
					return EINVAL;
				}
				json_field_name =
				    json_object_get(json_field, "name");
				if (!json_field_name) {
					avro_schema_decref(*schema);
					return EINVAL;
				}
				json_field_type =
				    json_object_get(json_field, "type");
				if (!json_field_type) {
					avro_schema_decref(*schema);
					return EINVAL;
				}
				field_rval =
				    avro_schema_from_json_t(json_field_type,
							    &json_field_type_schema,
							    error);
				if (field_rval) {
					avro_schema_decref(*schema);
					return field_rval;
				}
				field_rval =
				    avro_schema_record_field_append(*schema,
								    json_string_value
								    (json_field_name),
								    json_field_type_schema);
				avro_schema_decref(json_field_type_schema);
				if (field_rval != 0) {
					avro_schema_decref(*schema);
					return field_rval;
				}
			}
		}
		break;

	case AVRO_ENUM:
		{
			json_t *json_name = json_object_get(json, "name");
			json_t *json_symbols = json_object_get(json, "symbols");
			const char *name;
			unsigned int num_symbols;

			if (!json_is_string(json_name)) {
				return EINVAL;
			}
			if (!json_is_array(json_symbols)) {
				return EINVAL;
			}

			name = json_string_value(json_name);
			if (!name) {
				return EINVAL;
			}
			num_symbols = json_array_size(json_symbols);
			if (num_symbols == 0) {
				return EINVAL;
			}
			*schema = avro_schema_enum(name);
			if (save_named_schemas(name, *schema, error)) {
				return ENOMEM;
			}
			for (i = 0; i < num_symbols; i++) {
				int enum_rval;
				json_t *json_symbol =
				    json_array_get(json_symbols, i);
				const char *symbol;
				if (!json_is_string(json_symbol)) {
					avro_schema_decref(*schema);
					return EINVAL;
				}
				symbol = json_string_value(json_symbol);
				enum_rval =
				    avro_schema_enum_symbol_append(*schema,
								   symbol);
				if (enum_rval != 0) {
					avro_schema_decref(*schema);
					return enum_rval;
				}
			}
		}
		break;

	case AVRO_ARRAY:
		{
			int items_rval;
			json_t *json_items = json_object_get(json, "items");
			avro_schema_t items_schema;
			if (!json_items) {
				return EINVAL;
			}
			items_rval =
			    avro_schema_from_json_t(json_items, &items_schema,
						    error);
			if (items_rval) {
				return items_rval;
			}
			*schema = avro_schema_array(items_schema);
			avro_schema_decref(items_schema);
		}
		break;

	case AVRO_MAP:
		{
			int values_rval;
			json_t *json_values = json_object_get(json, "values");
			avro_schema_t values_schema;

			if (!json_values) {
				return EINVAL;
			}
			values_rval =
			    avro_schema_from_json_t(json_values, &values_schema,
						    error);
			if (values_rval) {
				return values_rval;
			}
			*schema = avro_schema_map(values_schema);
			avro_schema_decref(values_schema);
		}
		break;

	case AVRO_UNION:
		{
			unsigned int num_schemas = json_array_size(json);
			avro_schema_t s;
			if (num_schemas == 0) {
				return EINVAL;
			}
			*schema = avro_schema_union();
			for (i = 0; i < num_schemas; i++) {
				int schema_rval;
				json_t *schema_json = json_array_get(json, i);
				if (!schema_json) {
					return EINVAL;
				}
				schema_rval =
				    avro_schema_from_json_t(schema_json, &s,
							    error);
				if (schema_rval != 0) {
					avro_schema_decref(*schema);
					return schema_rval;
				}
				schema_rval =
				    avro_schema_union_append(*schema, s);
				avro_schema_decref(s);
				if (schema_rval != 0) {
					avro_schema_decref(*schema);
					return schema_rval;
				}
			}
		}
		break;

	case AVRO_FIXED:
		{
			json_t *json_size = json_object_get(json, "size");
			json_t *json_name = json_object_get(json, "name");
			int size;
			const char *name;
			if (!json_is_integer(json_size)) {
				return EINVAL;
			}
			if (!json_is_string(json_name)) {
				return EINVAL;
			}
			size = json_integer_value(json_size);
			name = json_string_value(json_name);
			*schema = avro_schema_fixed(name, size);
			if (save_named_schemas(name, *schema, error)) {
				return ENOMEM;
			}
		}
		break;

	default:
		return EINVAL;
	}
	return 0;
}

int
avro_schema_from_json(const char *jsontext, const int32_t len,
		      avro_schema_t * schema, avro_schema_error_t * e)
{
	json_t *root;
	int rval = 0;
	avro_schema_error_t error;

	AVRO_UNUSED(len);

	if (!jsontext || !schema) {
		return EINVAL;
	}

	error = malloc(sizeof(struct avro_schema_error_t_));
	if (!error) {
		return ENOMEM;
	}
	*e = error;

	error->named_schemas = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
	if (!error->named_schemas) {
		free(error);
		return ENOMEM;
	}

	root = json_loads(jsontext, &error->json_error);
	if (!root) {
		st_free_table(error->named_schemas);
		free(error);
		return EINVAL;
	}

	/*
	 * json_dumpf(root, stderr, 0); 
	 */
	rval = avro_schema_from_json_t(root, schema, e);
	json_decref(root);
	st_free_table(error->named_schemas);
	if (rval == 0) {
		/* no need for an error return */
		free(error);
	}
	return rval;
}

avro_schema_t avro_schema_copy(avro_schema_t schema)
{
	long i;
	avro_schema_t new_schema = NULL;
	if (!schema) {
		return NULL;
	}
	switch (avro_typeof(schema)) {
	case AVRO_STRING:
	case AVRO_BYTES:
	case AVRO_INT32:
	case AVRO_INT64:
	case AVRO_FLOAT:
	case AVRO_DOUBLE:
	case AVRO_BOOLEAN:
	case AVRO_NULL:
		/*
		 * No need to copy primitives since they're static 
		 */
		new_schema = schema;
		break;

	case AVRO_RECORD:
		{
			struct avro_record_schema_t *record_schema =
			    avro_schema_to_record(schema);
			new_schema =
			    avro_schema_record(record_schema->name,
					       record_schema->space);
			for (i = 0; i < record_schema->fields->num_entries; i++) {
				union {
					st_data_t data;
					struct avro_record_field_t *field;
				} val;
				st_lookup(record_schema->fields, i, &val.data);
				avro_schema_t type_copy =
				    avro_schema_copy(val.field->type);
				avro_schema_record_field_append(new_schema,
								val.field->name,
								type_copy);
			}
		}
		break;

	case AVRO_ENUM:
		{
			struct avro_enum_schema_t *enum_schema =
			    avro_schema_to_enum(schema);
			new_schema = avro_schema_enum(enum_schema->name);
			for (i = 0; i < enum_schema->symbols->num_entries; i++) {
				union {
					st_data_t data;
					char *sym;
				} val;
				st_lookup(enum_schema->symbols, i, &val.data);
				avro_schema_enum_symbol_append(new_schema,
							       val.sym);
			}
		}
		break;

	case AVRO_FIXED:
		{
			struct avro_fixed_schema_t *fixed_schema =
			    avro_schema_to_fixed(schema);
			new_schema =
			    avro_schema_fixed(fixed_schema->name,
					      fixed_schema->size);
		}
		break;

	case AVRO_MAP:
		{
			struct avro_map_schema_t *map_schema =
			    avro_schema_to_map(schema);
			avro_schema_t values_copy =
			    avro_schema_copy(map_schema->values);
			if (!values_copy) {
				return NULL;
			}
			new_schema = avro_schema_map(values_copy);
		}
		break;

	case AVRO_ARRAY:
		{
			struct avro_array_schema_t *array_schema =
			    avro_schema_to_array(schema);
			avro_schema_t items_copy =
			    avro_schema_copy(array_schema->items);
			if (!items_copy) {
				return NULL;
			}
			new_schema = avro_schema_array(items_copy);
		}
		break;

	case AVRO_UNION:
		{
			struct avro_union_schema_t *union_schema =
			    avro_schema_to_union(schema);

			new_schema = avro_schema_union();
			for (i = 0; i < union_schema->branches->num_entries;
			     i++) {
				avro_schema_t schema_copy;
				union {
					st_data_t data;
					avro_schema_t schema;
				} val;
				st_lookup(union_schema->branches, i, &val.data);
				schema_copy = avro_schema_copy(val.schema);
				if (avro_schema_union_append
				    (new_schema, schema_copy)) {
					avro_schema_decref(new_schema);
					return NULL;
				}
			}
		}
		break;

	case AVRO_LINK:
		{
			struct avro_link_schema_t *link_schema =
			    avro_schema_to_link(schema);
			/*
			 * TODO: use an avro_schema_copy of to instead of pointing to
			 * the same reference 
			 */
			avro_schema_incref(link_schema->to);
			new_schema = avro_schema_link(link_schema->to);
		}
		break;

	default:
		return NULL;
	}
	return new_schema;
}

const char *avro_schema_name(const avro_schema_t schema)
{
	if (is_avro_record(schema)) {
		return (avro_schema_to_record(schema))->name;
	} else if (is_avro_enum(schema)) {
		return (avro_schema_to_enum(schema))->name;
	} else if (is_avro_fixed(schema)) {
		return (avro_schema_to_fixed(schema))->name;
	}
	return NULL;
}

/* simple helper for writing strings */
static int avro_write_str(avro_writer_t out, const char *str)
{
	return avro_write(out, (char *)str, strlen(str));
}

static int write_field(avro_writer_t out, struct avro_record_field_t *field)
{
	int rval;
	check(rval, avro_write_str(out, "{\"name\":\""));
	check(rval, avro_write_str(out, field->name));
	check(rval, avro_write_str(out, "\",\"type\":"));
	check(rval, avro_schema_to_json(field->type, out));
	return avro_write_str(out, "}");
}

static int write_record(avro_writer_t out, struct avro_record_schema_t *record)
{
	int rval;
	long i;

	check(rval, avro_write_str(out, "{\"type\":\"record\",\"name\":\""));
	check(rval, avro_write_str(out, record->name));
	check(rval, avro_write_str(out, "\","));
	if (record->space) {
		check(rval, avro_write_str(out, "\"namespace\":\""));
		check(rval, avro_write_str(out, record->space));
		check(rval, avro_write_str(out, "\","));
	}
	check(rval, avro_write_str(out, "\"fields\":["));
	for (i = 0; i < record->fields->num_entries; i++) {
		union {
			st_data_t data;
			struct avro_record_field_t *field;
		} val;
		st_lookup(record->fields, i, &val.data);
		if (i) {
			check(rval, avro_write_str(out, ","));
		}
		check(rval, write_field(out, val.field));
	}
	return avro_write_str(out, "]}");
}