summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/avro/src/schema.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--fluent-bit/lib/avro/src/schema.c1897
1 files changed, 1897 insertions, 0 deletions
diff --git a/fluent-bit/lib/avro/src/schema.c b/fluent-bit/lib/avro/src/schema.c
new file mode 100644
index 00000000..7b389002
--- /dev/null
+++ b/fluent-bit/lib/avro/src/schema.c
@@ -0,0 +1,1897 @@
+/*
+ * 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
+ *
+ * https://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 "avro/allocation.h"
+#include "avro/refcount.h"
+#include "avro/errors.h"
+#include "avro/io.h"
+#include "avro/legacy.h"
+#include "avro/schema.h"
+#include "avro_private.h"
+#include <avro/platform.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "jansson.h"
+#include "st.h"
+#include "schema.h"
+
+#define DEFAULT_TABLE_SIZE 32
+
+/* forward declaration */
+static int
+avro_schema_to_json2(const avro_schema_t schema, avro_writer_t out,
+ const char *parent_namespace);
+
+static void avro_schema_init(avro_schema_t schema, avro_type_t type)
+{
+ schema->type = type;
+ schema->class_type = AVRO_SCHEMA;
+ avro_refcount_set(&schema->refcount, 1);
+}
+
+static int is_avro_id(const char *name)
+{
+ size_t i, len;
+ if (name) {
+ len = strlen(name);
+ if (len < 1) {
+ return 0;
+ }
+ for (i = 0; i < len; i++) {
+ if (!(isalpha(name[i])
+ || name[i] == '_' || (i && isdigit(name[i])))) {
+ return 0;
+ }
+ }
+ /*
+ * starts with [A-Za-z_] subsequent [A-Za-z0-9_]
+ */
+ return 1;
+ }
+ return 0;
+}
+
+/* Splits a qualified name by the last period, e.g. fullname "foo.bar.Baz" into
+ * name "Baz" and namespace "foo.bar". Sets name_out to the name part (pointing
+ * to a later position in the buffer that was passed in), and returns the
+ * namespace (as a newly allocated buffer using Avro's allocator). */
+static char *split_namespace_name(const char *fullname, const char **name_out)
+{
+ char *last_dot = strrchr(fullname, '.');
+ if (last_dot == NULL) {
+ *name_out = fullname;
+ return NULL;
+ } else {
+ *name_out = last_dot + 1;
+ return avro_strndup(fullname, last_dot - fullname);
+ }
+}
+
+static int record_free_foreach(int i, struct avro_record_field_t *field,
+ void *arg)
+{
+ AVRO_UNUSED(i);
+ AVRO_UNUSED(arg);
+
+ avro_str_free(field->name);
+ avro_schema_decref(field->type);
+ avro_freet(struct avro_record_field_t, field);
+ return ST_DELETE;
+}
+
+static int enum_free_foreach(int i, char *sym, void *arg)
+{
+ AVRO_UNUSED(i);
+ AVRO_UNUSED(arg);
+
+ avro_str_free(sym);
+ return ST_DELETE;
+}
+
+static int union_free_foreach(int i, avro_schema_t schema, void *arg)
+{
+ AVRO_UNUSED(i);
+ AVRO_UNUSED(arg);
+
+ avro_schema_decref(schema);
+ return ST_DELETE;
+}
+
+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);
+ /* Since we didn't increment the
+ * reference count of the target
+ * schema when we created the link, we
+ * should not decrement the reference
+ * count of the target schema when we
+ * free the link.
+ */
+ avro_freet(struct avro_link_schema_t, link);
+ }
+ break;
+ }
+ }
+}
+
+avro_schema_t avro_schema_incref(avro_schema_t schema)
+{
+ if (schema) {
+ avro_refcount_inc(&schema->refcount);
+ }
+ return schema;
+}
+
+int
+avro_schema_decref(avro_schema_t schema)
+{
+ if (schema && avro_refcount_dec(&schema->refcount)) {
+ avro_schema_free(schema);
+ return 0;
+ }
+ return 1;
+}
+
+avro_schema_t avro_schema_string(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_STRING,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_bytes(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_BYTES,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_int(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_INT32,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_long(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_INT64,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_float(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_FLOAT,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_double(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_DOUBLE,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_boolean(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_BOOLEAN,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_null(void)
+{
+ static struct avro_obj_t obj = {
+ AVRO_NULL,
+ AVRO_SCHEMA,
+ 1
+ };
+ return avro_schema_incref(&obj);
+}
+
+avro_schema_t avro_schema_fixed(const char *name, const int64_t size)
+{
+ return avro_schema_fixed_ns(name, NULL, size);
+}
+
+avro_schema_t avro_schema_fixed_ns(const char *name, const char *space,
+ const int64_t size)
+{
+ if (!is_avro_id(name)) {
+ avro_set_error("Invalid Avro identifier");
+ return NULL;
+ }
+
+ struct avro_fixed_schema_t *fixed =
+ (struct avro_fixed_schema_t *) avro_new(struct avro_fixed_schema_t);
+ if (!fixed) {
+ avro_set_error("Cannot allocate new fixed schema");
+ return NULL;
+ }
+ fixed->name = avro_strdup(name);
+ if (!fixed->name) {
+ avro_set_error("Cannot allocate new fixed schema");
+ avro_freet(struct avro_fixed_schema_t, fixed);
+ return NULL;
+ }
+ fixed->space = space ? avro_strdup(space) : NULL;
+ if (space && !fixed->space) {
+ avro_set_error("Cannot allocate new fixed schema");
+ avro_str_free((char *) fixed->name);
+ avro_freet(struct avro_fixed_schema_t, fixed);
+ return NULL;
+ }
+ fixed->size = size;
+ avro_schema_init(&fixed->obj, AVRO_FIXED);
+ return &fixed->obj;
+}
+
+int64_t avro_schema_fixed_size(const avro_schema_t fixed)
+{
+ return avro_schema_to_fixed(fixed)->size;
+}
+
+avro_schema_t avro_schema_union(void)
+{
+ struct avro_union_schema_t *schema =
+ (struct avro_union_schema_t *) avro_new(struct avro_union_schema_t);
+ if (!schema) {
+ avro_set_error("Cannot allocate new union schema");
+ return NULL;
+ }
+ schema->branches = st_init_numtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!schema->branches) {
+ avro_set_error("Cannot allocate new union schema");
+ avro_freet(struct avro_union_schema_t, schema);
+ return NULL;
+ }
+ schema->branches_byname =
+ st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!schema->branches_byname) {
+ avro_set_error("Cannot allocate new union schema");
+ st_free_table(schema->branches);
+ avro_freet(struct avro_union_schema_t, 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)
+{
+ check_param(EINVAL, is_avro_schema(union_schema), "union schema");
+ check_param(EINVAL, is_avro_union(union_schema), "union schema");
+ check_param(EINVAL, is_avro_schema(schema), "schema");
+
+ struct avro_union_schema_t *unionp = avro_schema_to_union(union_schema);
+ int new_index = unionp->branches->num_entries;
+ st_insert(unionp->branches, new_index, (st_data_t) schema);
+ const char *name = avro_schema_type_name(schema);
+ st_insert(unionp->branches_byname, (st_data_t) name,
+ (st_data_t) new_index);
+ avro_schema_incref(schema);
+ return 0;
+}
+
+size_t avro_schema_union_size(const avro_schema_t union_schema)
+{
+ check_param(EINVAL, is_avro_schema(union_schema), "union schema");
+ check_param(EINVAL, is_avro_union(union_schema), "union schema");
+ struct avro_union_schema_t *unionp = avro_schema_to_union(union_schema);
+ return unionp->branches->num_entries;
+}
+
+avro_schema_t avro_schema_union_branch(avro_schema_t unionp,
+ int branch_index)
+{
+ union {
+ st_data_t data;
+ avro_schema_t schema;
+ } val;
+ if (st_lookup(avro_schema_to_union(unionp)->branches,
+ branch_index, &val.data)) {
+ return val.schema;
+ } else {
+ avro_set_error("No union branch for discriminant %d",
+ branch_index);
+ return NULL;
+ }
+}
+
+avro_schema_t avro_schema_union_branch_by_name
+(avro_schema_t unionp, int *branch_index, const char *name)
+{
+ union {
+ st_data_t data;
+ int branch_index;
+ } val;
+
+ if (!st_lookup(avro_schema_to_union(unionp)->branches_byname,
+ (st_data_t) name, &val.data)) {
+ avro_set_error("No union branch named %s", name);
+ return NULL;
+ }
+
+ if (branch_index != NULL) {
+ *branch_index = val.branch_index;
+ }
+ return avro_schema_union_branch(unionp, val.branch_index);
+}
+
+avro_schema_t avro_schema_array(const avro_schema_t items)
+{
+ struct avro_array_schema_t *array =
+ (struct avro_array_schema_t *) avro_new(struct avro_array_schema_t);
+ if (!array) {
+ avro_set_error("Cannot allocate new array schema");
+ return NULL;
+ }
+ array->items = avro_schema_incref(items);
+ avro_schema_init(&array->obj, AVRO_ARRAY);
+ return &array->obj;
+}
+
+avro_schema_t avro_schema_array_items(avro_schema_t array)
+{
+ return avro_schema_to_array(array)->items;
+}
+
+avro_schema_t avro_schema_map(const avro_schema_t values)
+{
+ struct avro_map_schema_t *map =
+ (struct avro_map_schema_t *) avro_new(struct avro_map_schema_t);
+ if (!map) {
+ avro_set_error("Cannot allocate new map schema");
+ return NULL;
+ }
+ map->values = avro_schema_incref(values);
+ avro_schema_init(&map->obj, AVRO_MAP);
+ return &map->obj;
+}
+
+avro_schema_t avro_schema_map_values(avro_schema_t map)
+{
+ return avro_schema_to_map(map)->values;
+}
+
+avro_schema_t avro_schema_enum(const char *name)
+{
+ return avro_schema_enum_ns(name, NULL);
+}
+
+avro_schema_t avro_schema_enum_ns(const char *name, const char *space)
+{
+ if (!is_avro_id(name)) {
+ avro_set_error("Invalid Avro identifier");
+ return NULL;
+ }
+
+ struct avro_enum_schema_t *enump = (struct avro_enum_schema_t *) avro_new(struct avro_enum_schema_t);
+ if (!enump) {
+ avro_set_error("Cannot allocate new enum schema");
+ return NULL;
+ }
+ enump->name = avro_strdup(name);
+ if (!enump->name) {
+ avro_set_error("Cannot allocate new enum schema");
+ avro_freet(struct avro_enum_schema_t, enump);
+ return NULL;
+ }
+ enump->space = space ? avro_strdup(space) : NULL;
+ if (space && !enump->space) {
+ avro_set_error("Cannot allocate new enum schema");
+ avro_str_free(enump->name);
+ avro_freet(struct avro_enum_schema_t, enump);
+ return NULL;
+ }
+ enump->symbols = st_init_numtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!enump->symbols) {
+ avro_set_error("Cannot allocate new enum schema");
+ if (enump->space) avro_str_free(enump->space);
+ avro_str_free(enump->name);
+ avro_freet(struct avro_enum_schema_t, enump);
+ return NULL;
+ }
+ enump->symbols_byname = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!enump->symbols_byname) {
+ avro_set_error("Cannot allocate new enum schema");
+ st_free_table(enump->symbols);
+ if (enump->space) avro_str_free(enump->space);
+ avro_str_free(enump->name);
+ avro_freet(struct avro_enum_schema_t, enump);
+ return NULL;
+ }
+ avro_schema_init(&enump->obj, AVRO_ENUM);
+ return &enump->obj;
+}
+
+const char *avro_schema_enum_get(const avro_schema_t enump,
+ int index)
+{
+ union {
+ st_data_t data;
+ char *sym;
+ } val;
+ st_lookup(avro_schema_to_enum(enump)->symbols, index, &val.data);
+ return val.sym;
+}
+
+int avro_schema_enum_get_by_name(const avro_schema_t enump,
+ const char *symbol_name)
+{
+ union {
+ st_data_t data;
+ long idx;
+ } val;
+
+ if (st_lookup(avro_schema_to_enum(enump)->symbols_byname,
+ (st_data_t) symbol_name, &val.data)) {
+ return val.idx;
+ } else {
+ avro_set_error("No enum symbol named %s", symbol_name);
+ return -1;
+ }
+}
+
+int
+avro_schema_enum_symbol_append(const avro_schema_t enum_schema,
+ const char *symbol)
+{
+ check_param(EINVAL, is_avro_schema(enum_schema), "enum schema");
+ check_param(EINVAL, is_avro_enum(enum_schema), "enum schema");
+ check_param(EINVAL, symbol, "symbol");
+
+ char *sym;
+ long idx;
+ struct avro_enum_schema_t *enump = avro_schema_to_enum(enum_schema);
+ sym = avro_strdup(symbol);
+ if (!sym) {
+ avro_set_error("Cannot create copy of symbol name");
+ 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_enum_number_of_symbols(const avro_schema_t enum_schema)
+{
+ check_param(EINVAL, is_avro_schema(enum_schema), "enum schema");
+ check_param(EINVAL, is_avro_enum(enum_schema), "enum schema");
+
+ struct avro_enum_schema_t *enump = avro_schema_to_enum(enum_schema);
+ return enump->symbols->num_entries;
+}
+
+int
+avro_schema_record_field_append(const avro_schema_t record_schema,
+ const char *field_name,
+ const avro_schema_t field_schema)
+{
+ check_param(EINVAL, is_avro_schema(record_schema), "record schema");
+ check_param(EINVAL, is_avro_record(record_schema), "record schema");
+ check_param(EINVAL, field_name, "field name");
+ check_param(EINVAL, is_avro_schema(field_schema), "field schema");
+
+ if (!is_avro_id(field_name)) {
+ avro_set_error("Invalid Avro identifier");
+ return EINVAL;
+ }
+
+ if (record_schema == field_schema) {
+ avro_set_error("Cannot create a circular schema");
+ return EINVAL;
+ }
+
+ struct avro_record_schema_t *record = avro_schema_to_record(record_schema);
+ struct avro_record_field_t *new_field = (struct avro_record_field_t *) avro_new(struct avro_record_field_t);
+ if (!new_field) {
+ avro_set_error("Cannot allocate new record field");
+ return ENOMEM;
+ }
+ new_field->index = record->fields->num_entries;
+ new_field->name = avro_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)
+{
+ if (!is_avro_id(name)) {
+ avro_set_error("Invalid Avro identifier");
+ return NULL;
+ }
+
+ struct avro_record_schema_t *record = (struct avro_record_schema_t *) avro_new(struct avro_record_schema_t);
+ if (!record) {
+ avro_set_error("Cannot allocate new record schema");
+ return NULL;
+ }
+ record->name = avro_strdup(name);
+ if (!record->name) {
+ avro_set_error("Cannot allocate new record schema");
+ avro_freet(struct avro_record_schema_t, record);
+ return NULL;
+ }
+ record->space = space ? avro_strdup(space) : NULL;
+ if (space && !record->space) {
+ avro_set_error("Cannot allocate new record schema");
+ avro_str_free(record->name);
+ avro_freet(struct avro_record_schema_t, record);
+ return NULL;
+ }
+ record->fields = st_init_numtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!record->fields) {
+ avro_set_error("Cannot allocate new record schema");
+ if (record->space) {
+ avro_str_free(record->space);
+ }
+ avro_str_free(record->name);
+ avro_freet(struct avro_record_schema_t, record);
+ return NULL;
+ }
+ record->fields_byname = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!record->fields_byname) {
+ avro_set_error("Cannot allocate new record schema");
+ st_free_table(record->fields);
+ if (record->space) {
+ avro_str_free(record->space);
+ }
+ avro_str_free(record->name);
+ avro_freet(struct avro_record_schema_t, record);
+ return NULL;
+ }
+
+ avro_schema_init(&record->obj, AVRO_RECORD);
+ return &record->obj;
+}
+
+size_t avro_schema_record_size(const avro_schema_t record)
+{
+ return avro_schema_to_record(record)->fields->num_entries;
+}
+
+avro_schema_t avro_schema_record_field_get(const avro_schema_t
+ record, const char *field_name)
+{
+ union {
+ st_data_t data;
+ struct avro_record_field_t *field;
+ } val;
+ st_lookup(avro_schema_to_record(record)->fields_byname,
+ (st_data_t) field_name, &val.data);
+ return val.field->type;
+}
+
+int avro_schema_record_field_get_index(const avro_schema_t schema,
+ const char *field_name)
+{
+ union {
+ st_data_t data;
+ struct avro_record_field_t *field;
+ } val;
+ if (st_lookup(avro_schema_to_record(schema)->fields_byname,
+ (st_data_t) field_name, &val.data)) {
+ return val.field->index;
+ }
+
+ avro_set_error("No field named %s in record", field_name);
+ return -1;
+}
+
+const char *avro_schema_record_field_name(const avro_schema_t schema, int index)
+{
+ union {
+ st_data_t data;
+ struct avro_record_field_t *field;
+ } val;
+ st_lookup(avro_schema_to_record(schema)->fields, index, &val.data);
+ return val.field->name;
+}
+
+avro_schema_t avro_schema_record_field_get_by_index
+(const avro_schema_t record, int index)
+{
+ union {
+ st_data_t data;
+ struct avro_record_field_t *field;
+ } val;
+ st_lookup(avro_schema_to_record(record)->fields, index, &val.data);
+ return val.field->type;
+}
+
+avro_schema_t avro_schema_link(avro_schema_t to)
+{
+ if (!is_avro_named_type(to)) {
+ avro_set_error("Can only link to named types");
+ return NULL;
+ }
+
+ struct avro_link_schema_t *link = (struct avro_link_schema_t *) avro_new(struct avro_link_schema_t);
+ if (!link) {
+ avro_set_error("Cannot allocate new link schema");
+ return NULL;
+ }
+
+ /* Do not increment the reference count of target schema
+ * pointed to by the AVRO_LINK. AVRO_LINKs are only valid
+ * internal to a schema. The target schema pointed to by a
+ * link will be valid as long as the top-level schema is
+ * valid. Similarly, the link will be valid as long as the
+ * top-level schema is valid. Therefore the validity of the
+ * link ensures the validity of its target, and we don't need
+ * an additional reference count on the target. This mechanism
+ * of an implied validity also breaks reference count cycles
+ * for recursive schemas, which result in memory leaks.
+ */
+ link->to = to;
+ avro_schema_init(&link->obj, AVRO_LINK);
+ return &link->obj;
+}
+
+avro_schema_t avro_schema_link_target(avro_schema_t schema)
+{
+ check_param(NULL, is_avro_schema(schema), "schema");
+ check_param(NULL, is_avro_link(schema), "schema");
+
+ struct avro_link_schema_t *link = avro_schema_to_link(schema);
+ return link->to;
+}
+
+static const char *
+qualify_name(const char *name, const char *namespace)
+{
+ char *full_name;
+ if (namespace != NULL && strchr(name, '.') == NULL) {
+ full_name = avro_str_alloc(strlen(name) + strlen(namespace) + 2);
+ sprintf(full_name, "%s.%s", namespace, name);
+ } else {
+ full_name = avro_strdup(name);
+ }
+ return full_name;
+}
+
+static int
+save_named_schemas(const avro_schema_t schema, st_table *st)
+{
+ const char *name = avro_schema_name(schema);
+ const char *namespace = avro_schema_namespace(schema);
+ const char *full_name = qualify_name(name, namespace);
+ int rval = st_insert(st, (st_data_t) full_name, (st_data_t) schema);
+ return rval;
+}
+
+static avro_schema_t
+find_named_schemas(const char *name, const char *namespace, st_table *st)
+{
+ union {
+ avro_schema_t schema;
+ st_data_t data;
+ } val;
+ const char *full_name = qualify_name(name, namespace);
+ int rval = st_lookup(st, (st_data_t) full_name, &(val.data));
+ avro_str_free((char *)full_name);
+ if (rval) {
+ return val.schema;
+ }
+ avro_set_error("No schema type named %s", name);
+ return NULL;
+};
+
+static int
+avro_type_from_json_t(json_t *json, avro_type_t *type,
+ st_table *named_schemas, avro_schema_t *named_type,
+ const char *namespace)
+{
+ 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)) {
+ avro_set_error("\"type\" field must be a string");
+ return EINVAL;
+ }
+ type_str = json_string_value(json_type);
+ if (!type_str) {
+ avro_set_error("\"type\" field must be a string");
+ 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, namespace, named_schemas))) {
+ *type = AVRO_LINK;
+ } else {
+ avro_set_error("Unknown Avro \"type\": %s", type_str);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+avro_schema_from_json_t(json_t *json, avro_schema_t *schema,
+ st_table *named_schemas, const char *parent_namespace)
+{
+#ifdef _WIN32
+ #pragma message("#warning: Bug: '0' is not of type avro_type_t.")
+#else
+ #warning "Bug: '0' is not of type avro_type_t."
+#endif
+ /* We should really have an "AVRO_INVALID" type in
+ * avro_type_t. Suppress warning below in which we set type to 0.
+ */
+ avro_type_t type = (avro_type_t) 0;
+ unsigned int i;
+ avro_schema_t named_type = NULL;
+
+ if (avro_type_from_json_t(json, &type, named_schemas, &named_type, parent_namespace)) {
+ return EINVAL;
+ }
+
+ switch (type) {
+ case AVRO_LINK:
+ *schema = avro_schema_link(named_type);
+ 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 *fullname, *name;
+
+ if (!json_is_string(json_name)) {
+ avro_set_error("Record type must have a \"name\"");
+ return EINVAL;
+ }
+ if (!json_is_array(json_fields)) {
+ avro_set_error("Record type must have \"fields\"");
+ return EINVAL;
+ }
+ num_fields = json_array_size(json_fields);
+ fullname = json_string_value(json_name);
+ if (!fullname) {
+ avro_set_error("Record type must have a \"name\"");
+ return EINVAL;
+ }
+
+ if (strchr(fullname, '.')) {
+ char *namespace = split_namespace_name(fullname, &name);
+ *schema = avro_schema_record(name, namespace);
+ avro_str_free(namespace);
+ } else if (json_is_string(json_namespace)) {
+ const char *namespace = json_string_value(json_namespace);
+ if (strlen(namespace) == 0) {
+ namespace = NULL;
+ }
+ *schema = avro_schema_record(fullname, namespace);
+ } else {
+ *schema = avro_schema_record(fullname, parent_namespace);
+ }
+
+ if (*schema == NULL) {
+ return ENOMEM;
+ }
+ if (save_named_schemas(*schema, named_schemas)) {
+ avro_set_error("Cannot save record schema");
+ 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_set_error("Record field %d must be an array", i);
+ avro_schema_decref(*schema);
+ return EINVAL;
+ }
+ json_field_name =
+ json_object_get(json_field, "name");
+ if (!json_field_name) {
+ avro_set_error("Record field %d must have a \"name\"", i);
+ avro_schema_decref(*schema);
+ return EINVAL;
+ }
+ json_field_type =
+ json_object_get(json_field, "type");
+ if (!json_field_type) {
+ avro_set_error("Record field %d must have a \"type\"", i);
+ avro_schema_decref(*schema);
+ return EINVAL;
+ }
+ field_rval =
+ avro_schema_from_json_t(json_field_type,
+ &json_field_type_schema,
+ named_schemas,
+ avro_schema_namespace(*schema));
+ 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");
+ json_t *json_namespace = json_object_get(json, "namespace");
+ const char *fullname, *name;
+ unsigned int num_symbols;
+
+ if (!json_is_string(json_name)) {
+ avro_set_error("Enum type must have a \"name\"");
+ return EINVAL;
+ }
+ if (!json_is_array(json_symbols)) {
+ avro_set_error("Enum type must have \"symbols\"");
+ return EINVAL;
+ }
+
+ fullname = json_string_value(json_name);
+ if (!fullname) {
+ avro_set_error("Enum type must have a \"name\"");
+ return EINVAL;
+ }
+ num_symbols = json_array_size(json_symbols);
+ if (num_symbols == 0) {
+ avro_set_error("Enum type must have at least one symbol");
+ return EINVAL;
+ }
+
+ if (strchr(fullname, '.')) {
+ char *namespace;
+ namespace = split_namespace_name(fullname, &name);
+ *schema = avro_schema_enum_ns(name, namespace);
+ avro_str_free(namespace);
+ } else if (json_is_string(json_namespace)) {
+ const char *namespace = json_string_value(json_namespace);
+ if (strlen(namespace) == 0) {
+ namespace = NULL;
+ }
+ *schema = avro_schema_enum_ns(fullname, namespace);
+ } else {
+ *schema = avro_schema_enum_ns(fullname, parent_namespace);
+ }
+
+ if (*schema == NULL) {
+ return ENOMEM;
+ }
+ if (save_named_schemas(*schema, named_schemas)) {
+ avro_set_error("Cannot save enum schema");
+ 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_set_error("Enum symbol %d must be a string", i);
+ 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) {
+ avro_set_error("Array type must have \"items\"");
+ return EINVAL;
+ }
+ items_rval =
+ avro_schema_from_json_t(json_items, &items_schema,
+ named_schemas, parent_namespace);
+ 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) {
+ avro_set_error("Map type must have \"values\"");
+ return EINVAL;
+ }
+ values_rval =
+ avro_schema_from_json_t(json_values, &values_schema,
+ named_schemas, parent_namespace);
+ 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) {
+ avro_set_error("Union type must have at least one branch");
+ 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) {
+ avro_set_error("Cannot retrieve branch JSON");
+ return EINVAL;
+ }
+ schema_rval =
+ avro_schema_from_json_t(schema_json, &s,
+ named_schemas, parent_namespace);
+ 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");
+ json_t *json_namespace = json_object_get(json, "namespace");
+ json_int_t size;
+ const char *fullname, *name;
+ if (!json_is_integer(json_size)) {
+ avro_set_error("Fixed type must have a \"size\"");
+ return EINVAL;
+ }
+ if (!json_is_string(json_name)) {
+ avro_set_error("Fixed type must have a \"name\"");
+ return EINVAL;
+ }
+ size = json_integer_value(json_size);
+ fullname = json_string_value(json_name);
+
+ if (strchr(fullname, '.')) {
+ char *namespace;
+ namespace = split_namespace_name(fullname, &name);
+ *schema = avro_schema_fixed_ns(name, namespace, (int64_t) size);
+ avro_str_free(namespace);
+ } else if (json_is_string(json_namespace)) {
+ const char *namespace = json_string_value(json_namespace);
+ if (strlen(namespace) == 0) {
+ namespace = NULL;
+ }
+ *schema = avro_schema_fixed_ns(fullname, namespace, (int64_t) size);
+ } else {
+ *schema = avro_schema_fixed_ns(fullname, parent_namespace, (int64_t) size);
+ }
+
+ if (*schema == NULL) {
+ return ENOMEM;
+ }
+ if (save_named_schemas(*schema, named_schemas)) {
+ avro_set_error("Cannot save fixed schema");
+ return ENOMEM;
+ }
+ }
+ break;
+
+ default:
+ avro_set_error("Unknown schema type");
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int named_schema_free_foreach(char *full_name, st_data_t value, st_data_t arg)
+{
+ AVRO_UNUSED(value);
+ AVRO_UNUSED(arg);
+
+ avro_str_free(full_name);
+ return ST_DELETE;
+}
+
+static int
+avro_schema_from_json_root(json_t *root, avro_schema_t *schema)
+{
+ int rval;
+ st_table *named_schemas;
+
+ named_schemas = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!named_schemas) {
+ avro_set_error("Cannot allocate named schema map");
+ json_decref(root);
+ return ENOMEM;
+ }
+
+ /* json_dumpf(root, stderr, 0); */
+ rval = avro_schema_from_json_t(root, schema, named_schemas, NULL);
+ json_decref(root);
+ st_foreach(named_schemas, HASH_FUNCTION_CAST named_schema_free_foreach, 0);
+ st_free_table(named_schemas);
+ return rval;
+}
+
+int
+avro_schema_from_json(const char *jsontext, const int32_t len,
+ avro_schema_t *schema, avro_schema_error_t *e)
+{
+ check_param(EINVAL, jsontext, "JSON text");
+ check_param(EINVAL, schema, "schema pointer");
+
+ json_t *root;
+ json_error_t json_error;
+
+ AVRO_UNUSED(len);
+ AVRO_UNUSED(e);
+
+ root = json_loads(jsontext, JSON_DECODE_ANY, &json_error);
+ if (!root) {
+ avro_set_error("Error parsing JSON: %s", json_error.text);
+ return EINVAL;
+ }
+
+ return avro_schema_from_json_root(root, schema);
+}
+
+int
+avro_schema_from_json_length(const char *jsontext, size_t length,
+ avro_schema_t *schema)
+{
+ check_param(EINVAL, jsontext, "JSON text");
+ check_param(EINVAL, schema, "schema pointer");
+
+ json_t *root;
+ json_error_t json_error;
+
+ root = json_loadb(jsontext, length, JSON_DECODE_ANY, &json_error);
+ if (!root) {
+ avro_set_error("Error parsing JSON: %s", json_error.text);
+ return EINVAL;
+ }
+
+ return avro_schema_from_json_root(root, schema);
+}
+
+avro_schema_t avro_schema_copy_root(avro_schema_t schema, st_table *named_schemas)
+{
+ 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);
+ if (save_named_schemas(new_schema, named_schemas)) {
+ avro_set_error("Cannot save enum schema");
+ return NULL;
+ }
+ 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_root(val.field->type, named_schemas);
+ avro_schema_record_field_append(new_schema,
+ val.field->name,
+ type_copy);
+ avro_schema_decref(type_copy);
+ }
+ }
+ break;
+
+ case AVRO_ENUM:
+ {
+ struct avro_enum_schema_t *enum_schema =
+ avro_schema_to_enum(schema);
+ new_schema = avro_schema_enum_ns(enum_schema->name,
+ enum_schema->space);
+ if (save_named_schemas(new_schema, named_schemas)) {
+ avro_set_error("Cannot save enum schema");
+ return NULL;
+ }
+ 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_ns(fixed_schema->name,
+ fixed_schema->space,
+ fixed_schema->size);
+ if (save_named_schemas(new_schema, named_schemas)) {
+ avro_set_error("Cannot save fixed schema");
+ return NULL;
+ }
+ }
+ break;
+
+ case AVRO_MAP:
+ {
+ struct avro_map_schema_t *map_schema =
+ avro_schema_to_map(schema);
+ avro_schema_t values_copy =
+ avro_schema_copy_root(map_schema->values, named_schemas);
+ if (!values_copy) {
+ return NULL;
+ }
+ new_schema = avro_schema_map(values_copy);
+ avro_schema_decref(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_root(array_schema->items, named_schemas);
+ if (!items_copy) {
+ return NULL;
+ }
+ new_schema = avro_schema_array(items_copy);
+ avro_schema_decref(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_root(val.schema, named_schemas);
+ if (avro_schema_union_append
+ (new_schema, schema_copy)) {
+ avro_schema_decref(new_schema);
+ return NULL;
+ }
+ avro_schema_decref(schema_copy);
+ }
+ }
+ break;
+
+ case AVRO_LINK:
+ {
+ struct avro_link_schema_t *link_schema =
+ avro_schema_to_link(schema);
+ avro_schema_t to;
+
+ to = find_named_schemas(avro_schema_name(link_schema->to),
+ avro_schema_namespace(link_schema->to),
+ named_schemas);
+ new_schema = avro_schema_link(to);
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+ return new_schema;
+}
+
+avro_schema_t avro_schema_copy(avro_schema_t schema)
+{
+ avro_schema_t new_schema;
+ st_table *named_schemas;
+
+ named_schemas = st_init_strtable_with_size(DEFAULT_TABLE_SIZE);
+ if (!named_schemas) {
+ avro_set_error("Cannot allocate named schema map");
+ return NULL;
+ }
+
+ new_schema = avro_schema_copy_root(schema, named_schemas);
+ st_foreach(named_schemas, HASH_FUNCTION_CAST named_schema_free_foreach, 0);
+ st_free_table(named_schemas);
+ return new_schema;
+}
+
+avro_schema_t avro_schema_get_subschema(const avro_schema_t schema,
+ const char *name)
+{
+ if (is_avro_record(schema)) {
+ const struct avro_record_schema_t *rschema =
+ avro_schema_to_record(schema);
+ union {
+ st_data_t data;
+ struct avro_record_field_t *field;
+ } field;
+
+ if (st_lookup(rschema->fields_byname,
+ (st_data_t) name, &field.data))
+ {
+ return field.field->type;
+ }
+
+ avro_set_error("No record field named %s", name);
+ return NULL;
+ } else if (is_avro_union(schema)) {
+ const struct avro_union_schema_t *uschema =
+ avro_schema_to_union(schema);
+ long i;
+
+ for (i = 0; i < uschema->branches->num_entries; i++) {
+ union {
+ st_data_t data;
+ avro_schema_t schema;
+ } val;
+ st_lookup(uschema->branches, i, &val.data);
+ if (strcmp(avro_schema_type_name(val.schema),
+ name) == 0)
+ {
+ return val.schema;
+ }
+ }
+
+ avro_set_error("No union branch named %s", name);
+ return NULL;
+ } else if (is_avro_array(schema)) {
+ if (strcmp(name, "[]") == 0) {
+ const struct avro_array_schema_t *aschema =
+ avro_schema_to_array(schema);
+ return aschema->items;
+ }
+
+ avro_set_error("Array subschema must be called \"[]\"");
+ return NULL;
+ } else if (is_avro_map(schema)) {
+ if (strcmp(name, "{}") == 0) {
+ const struct avro_map_schema_t *mschema =
+ avro_schema_to_map(schema);
+ return mschema->values;
+ }
+
+ avro_set_error("Map subschema must be called \"{}\"");
+ return NULL;
+ }
+
+ avro_set_error("Can only retrieve subschemas from record, union, array, or map");
+ return NULL;
+}
+
+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;
+ }
+ avro_set_error("Schema has no name");
+ return NULL;
+}
+
+const char *avro_schema_namespace(const avro_schema_t schema)
+{
+ if (is_avro_record(schema)) {
+ return (avro_schema_to_record(schema))->space;
+ } else if (is_avro_enum(schema)) {
+ return (avro_schema_to_enum(schema))->space;
+ } else if (is_avro_fixed(schema)) {
+ return (avro_schema_to_fixed(schema))->space;
+ }
+ return NULL;
+}
+
+const char *avro_schema_type_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;
+ } else if (is_avro_union(schema)) {
+ return "union";
+ } else if (is_avro_array(schema)) {
+ return "array";
+ } else if (is_avro_map(schema)) {
+ return "map";
+ } else if (is_avro_int32(schema)) {
+ return "int";
+ } else if (is_avro_int64(schema)) {
+ return "long";
+ } else if (is_avro_float(schema)) {
+ return "float";
+ } else if (is_avro_double(schema)) {
+ return "double";
+ } else if (is_avro_boolean(schema)) {
+ return "boolean";
+ } else if (is_avro_null(schema)) {
+ return "null";
+ } else if (is_avro_string(schema)) {
+ return "string";
+ } else if (is_avro_bytes(schema)) {
+ return "bytes";
+ } else if (is_avro_link(schema)) {
+ avro_schema_t target = avro_schema_link_target(schema);
+ return avro_schema_type_name(target);
+ }
+ avro_set_error("Unknown schema type");
+ return NULL;
+}
+
+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;
+ }
+}
+
+/* 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, const struct avro_record_field_t *field,
+ const char *parent_namespace)
+{
+ 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_json2(field->type, out, parent_namespace));
+ return avro_write_str(out, "}");
+}
+
+static int write_record(avro_writer_t out, const struct avro_record_schema_t *record,
+ const char *parent_namespace)
+{
+ 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 (nullstrcmp(record->space, parent_namespace)) {
+ check(rval, avro_write_str(out, "\"namespace\":\""));
+ if (record->space) {
+ 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, record->space));
+ }
+ return avro_write_str(out, "]}");
+}
+
+static int write_enum(avro_writer_t out, const struct avro_enum_schema_t *enump,
+ const char *parent_namespace)
+{
+ int rval;
+ long i;
+ check(rval, avro_write_str(out, "{\"type\":\"enum\",\"name\":\""));
+ check(rval, avro_write_str(out, enump->name));
+ check(rval, avro_write_str(out, "\","));
+ if (nullstrcmp(enump->space, parent_namespace)) {
+ check(rval, avro_write_str(out, "\"namespace\":\""));
+ if (enump->space) {
+ check(rval, avro_write_str(out, enump->space));
+ }
+ check(rval, avro_write_str(out, "\","));
+ }
+ check(rval, avro_write_str(out, "\"symbols\":["));
+
+ for (i = 0; i < enump->symbols->num_entries; i++) {
+ union {
+ st_data_t data;
+ char *sym;
+ } val;
+ st_lookup(enump->symbols, i, &val.data);
+ if (i) {
+ check(rval, avro_write_str(out, ","));
+ }
+ check(rval, avro_write_str(out, "\""));
+ check(rval, avro_write_str(out, val.sym));
+ check(rval, avro_write_str(out, "\""));
+ }
+ return avro_write_str(out, "]}");
+}
+
+static int write_fixed(avro_writer_t out, const struct avro_fixed_schema_t *fixed,
+ const char *parent_namespace)
+{
+ int rval;
+ char size[16];
+ check(rval, avro_write_str(out, "{\"type\":\"fixed\",\"name\":\""));
+ check(rval, avro_write_str(out, fixed->name));
+ check(rval, avro_write_str(out, "\","));
+ if (nullstrcmp(fixed->space, parent_namespace)) {
+ check(rval, avro_write_str(out, "\"namespace\":\""));
+ if (fixed->space) {
+ check(rval, avro_write_str(out, fixed->space));
+ }
+ check(rval, avro_write_str(out, "\","));
+ }
+ check(rval, avro_write_str(out, "\"size\":"));
+ snprintf(size, sizeof(size), "%" PRId64, fixed->size);
+ check(rval, avro_write_str(out, size));
+ return avro_write_str(out, "}");
+}
+
+static int write_map(avro_writer_t out, const struct avro_map_schema_t *map,
+ const char *parent_namespace)
+{
+ int rval;
+ check(rval, avro_write_str(out, "{\"type\":\"map\",\"values\":"));
+ check(rval, avro_schema_to_json2(map->values, out, parent_namespace));
+ return avro_write_str(out, "}");
+}
+static int write_array(avro_writer_t out, const struct avro_array_schema_t *array,
+ const char *parent_namespace)
+{
+ int rval;
+ check(rval, avro_write_str(out, "{\"type\":\"array\",\"items\":"));
+ check(rval, avro_schema_to_json2(array->items, out, parent_namespace));
+ return avro_write_str(out, "}");
+}
+static int write_union(avro_writer_t out, const struct avro_union_schema_t *unionp,
+ const char *parent_namespace)
+{
+ int rval;
+ long i;
+ check(rval, avro_write_str(out, "["));
+
+ for (i = 0; i < unionp->branches->num_entries; i++) {
+ union {
+ st_data_t data;
+ avro_schema_t schema;
+ } val;
+ st_lookup(unionp->branches, i, &val.data);
+ if (i) {
+ check(rval, avro_write_str(out, ","));
+ }
+ check(rval, avro_schema_to_json2(val.schema, out, parent_namespace));
+ }
+ return avro_write_str(out, "]");
+}
+static int write_link(avro_writer_t out, const struct avro_link_schema_t *link,
+ const char *parent_namespace)
+{
+ int rval;
+ check(rval, avro_write_str(out, "\""));
+ const char *namespace = avro_schema_namespace(link->to);
+ if (namespace && nullstrcmp(namespace, parent_namespace)) {
+ check(rval, avro_write_str(out, namespace));
+ check(rval, avro_write_str(out, "."));
+ }
+ check(rval, avro_write_str(out, avro_schema_name(link->to)));
+ return avro_write_str(out, "\"");
+}
+
+static int
+avro_schema_to_json2(const avro_schema_t schema, avro_writer_t out,
+ const char *parent_namespace)
+{
+ 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), parent_namespace);
+ case AVRO_ENUM:
+ return write_enum(out, avro_schema_to_enum(schema), parent_namespace);
+ case AVRO_FIXED:
+ return write_fixed(out, avro_schema_to_fixed(schema), parent_namespace);
+ case AVRO_MAP:
+ return write_map(out, avro_schema_to_map(schema), parent_namespace);
+ case AVRO_ARRAY:
+ return write_array(out, avro_schema_to_array(schema), parent_namespace);
+ case AVRO_UNION:
+ return write_union(out, avro_schema_to_union(schema), parent_namespace);
+ case AVRO_LINK:
+ return write_link(out, avro_schema_to_link(schema), parent_namespace);
+ }
+
+ if (is_avro_primitive(schema)) {
+ return avro_write_str(out, "\"}");
+ }
+ avro_set_error("Unknown schema type");
+ return EINVAL;
+}
+
+int avro_schema_to_json(const avro_schema_t schema, avro_writer_t out)
+{
+ return avro_schema_to_json2(schema, out, NULL);
+}