diff options
Diffstat (limited to '')
-rw-r--r-- | fluent-bit/lib/avro/src/schema.c | 1897 |
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); +} |