diff options
Diffstat (limited to 'fluent-bit/lib/avro/src')
67 files changed, 28371 insertions, 0 deletions
diff --git a/fluent-bit/lib/avro/src/.gitignore b/fluent-bit/lib/avro/src/.gitignore new file mode 100644 index 000000000..e278a8b91 --- /dev/null +++ b/fluent-bit/lib/avro/src/.gitignore @@ -0,0 +1,2 @@ +avro-c.pc +avropipe diff --git a/fluent-bit/lib/avro/src/CMakeLists.txt b/fluent-bit/lib/avro/src/CMakeLists.txt new file mode 100644 index 000000000..a911115f6 --- /dev/null +++ b/fluent-bit/lib/avro/src/CMakeLists.txt @@ -0,0 +1,158 @@ +# +# 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. +# + +set(AVRO_SRC + allocation.c + array.c + avro.h + avro/allocation.h + avro/basics.h + avro/consumer.h + avro/data.h + avro/errors.h + avro/generic.h + avro/io.h + avro/legacy.h + avro/refcount.h + avro/resolver.h + avro/schema.h + avro/value.h + avro_generic_internal.h + avro_private.h + codec.c + codec.h + consumer.c + consume-binary.c + datafile.c + datum.c + datum.h + datum_equal.c + datum_read.c + datum_size.c + datum_skip.c + datum_validate.c + datum_value.c + datum_write.c + dump.c + dump.h + encoding.h + encoding_binary.c + errors.c + generic.c + io.c + map.c + memoize.c + resolved-reader.c + resolved-writer.c + resolver.c + schema.c + schema.h + schema_equal.c + st.c + st.h + string.c + value.c + value-hash.c + value-json.c + value-read.c + value-sizeof.c + value-write.c + wrapped-buffer.c +) + + +source_group(Avro FILES ${AVRO_SRC}) + +# The version.sh script gives us a VERSION that uses colon as a +# separator; we need periods. + +string(REPLACE ":" "." LIBAVRO_DOT_VERSION ${LIBAVRO_VERSION}) + +add_library(avro-static STATIC ${AVRO_SRC}) +target_link_libraries(avro-static ${JANSSON_LIBRARIES} ${CODEC_LIBRARIES} ${THREADS_LIBRARIES}) +set_target_properties(avro-static PROPERTIES OUTPUT_NAME avro) + +if (NOT WIN32) +# TODO: Create Windows DLLs. See https://www.cmake.org/Wiki/BuildingWinDLL +add_library(avro-shared SHARED ${AVRO_SRC}) +target_link_libraries(avro-shared ${JANSSON_LIBRARIES} ${CODEC_LIBRARIES} ${THREADS_LIBRARIES}) +set_target_properties(avro-shared PROPERTIES + OUTPUT_NAME avro + VERSION ${LIBAVRO_DOT_VERSION} + SOVERSION ${LIBAVRO_SOVERSION}) +endif(NOT WIN32) + +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/avro.h + DESTINATION include) +install(DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/avro + DESTINATION include + FILES_MATCHING PATTERN "*.h") + +# updated by edsiper, avro version 77692d8c1 +# added by mcqueen to get the headers into the fluent-bit build root 07OCT2020 +file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/avro.h + DESTINATION ${CMAKE_BINARY_DIR}/include/) +file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/avro + DESTINATION ${CMAKE_BINARY_DIR}/include/ + FILES_MATCHING PATTERN "*.h") + +include(GNUInstallDirs) + +if (WIN32) +install(TARGETS avro-static + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) +else(WIN32) +install(TARGETS avro-static avro-shared + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) +endif(WIN32) + +# Install pkg-config file + +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(VERSION ${AVRO_VERSION}) +configure_file(avro-c.pc.in avro-c.pc) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/avro-c.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +add_executable(avrocat avrocat.c) +target_link_libraries(avrocat avro-static) +install(TARGETS avrocat RUNTIME DESTINATION bin) + +add_executable(avroappend avroappend.c) +target_link_libraries(avroappend avro-static) +install(TARGETS avroappend RUNTIME DESTINATION bin) + +if (NOT WIN32) +#TODO: Port getopt() to Windows to compile avropipe.c and avromod.c +add_executable(avropipe avropipe.c) +target_link_libraries(avropipe avro-static) +install(TARGETS avropipe RUNTIME DESTINATION bin) + +add_executable(avromod avromod.c) +target_link_libraries(avromod avro-static) +install(TARGETS avromod RUNTIME DESTINATION bin) +endif(NOT WIN32) diff --git a/fluent-bit/lib/avro/src/allocation.c b/fluent-bit/lib/avro/src/allocation.c new file mode 100644 index 000000000..2059f92a9 --- /dev/null +++ b/fluent-bit/lib/avro/src/allocation.c @@ -0,0 +1,116 @@ +/* + * 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 <stdlib.h> +#include <string.h> + +#include "avro_private.h" +#include "avro/allocation.h" +#include "avro/data.h" +#include "avro/legacy.h" + +static void * +avro_default_allocator(void *ud, void *ptr, size_t osize, size_t nsize) +{ + AVRO_UNUSED(ud); + AVRO_UNUSED(osize); + + if (nsize == 0) { + free(ptr); + return NULL; + } else { + return realloc(ptr, nsize); + } +} + +struct avro_allocator_state AVRO_CURRENT_ALLOCATOR = { + avro_default_allocator, + NULL +}; + +void avro_set_allocator(avro_allocator_t alloc, void *user_data) +{ + AVRO_CURRENT_ALLOCATOR.alloc = alloc; + AVRO_CURRENT_ALLOCATOR.user_data = user_data; +} + +void *avro_calloc(size_t count, size_t size) +{ + void *ptr = avro_malloc(count * size); + if (ptr != NULL) { + memset(ptr, 0, count * size); + } + return ptr; +} + +char *avro_str_alloc(size_t str_size) +{ + size_t buf_size = str_size + sizeof(size_t); + + void *buf = avro_malloc(buf_size); + if (buf == NULL) { + return NULL; + } + + size_t *size = (size_t *) buf; + char *new_str = (char *) (size + 1); + + *size = buf_size; + + return new_str; +} + +char *avro_strdup(const char *str) +{ + if (str == NULL) { + return NULL; + } + + size_t str_size = strlen(str)+1; + char *new_str = avro_str_alloc(str_size); + memcpy(new_str, str, str_size); + + //fprintf(stderr, "--- new %" PRIsz " %p %s\n", *size, new_str, new_str); + return new_str; +} + +char *avro_strndup(const char *str, size_t size) +{ + if (str == NULL) { + return NULL; + } + + char *new_str = avro_str_alloc(size + 1); + memcpy(new_str, str, size); + new_str[size] = '\0'; + + return new_str; +} + +void avro_str_free(char *str) +{ + size_t *size = ((size_t *) str) - 1; + //fprintf(stderr, "--- free %" PRIsz " %p %s\n", *size, str, str); + avro_free(size, *size); +} + + +void +avro_alloc_free_func(void *ptr, size_t sz) +{ + avro_free(ptr, sz); +} diff --git a/fluent-bit/lib/avro/src/array.c b/fluent-bit/lib/avro/src/array.c new file mode 100644 index 000000000..94dce295c --- /dev/null +++ b/fluent-bit/lib/avro/src/array.c @@ -0,0 +1,118 @@ +/* + * 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 <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro_private.h" + + +void avro_raw_array_init(avro_raw_array_t *array, size_t element_size) +{ + memset(array, 0, sizeof(avro_raw_array_t)); + array->element_size = element_size; +} + + +void avro_raw_array_done(avro_raw_array_t *array) +{ + if (array->data) { + avro_free(array->data, array->allocated_size); + } + memset(array, 0, sizeof(avro_raw_array_t)); +} + + +void avro_raw_array_clear(avro_raw_array_t *array) +{ + array->element_count = 0; +} + + +int +avro_raw_array_ensure_size(avro_raw_array_t *array, size_t desired_count) +{ + size_t required_size = array->element_size * desired_count; + if (array->allocated_size >= required_size) { + return 0; + } + + /* + * Double the old size when reallocating. + */ + + size_t new_size; + if (array->allocated_size == 0) { + /* + * Start with an arbitrary 10 items. + */ + + new_size = 10 * array->element_size; + } else { + new_size = array->allocated_size * 2; + } + + if (required_size > new_size) { + new_size = required_size; + } + + array->data = avro_realloc(array->data, array->allocated_size, new_size); + if (array->data == NULL) { + avro_set_error("Cannot allocate space in array for %" PRIsz " elements", + desired_count); + return ENOMEM; + } + array->allocated_size = new_size; + + return 0; +} + + +int +avro_raw_array_ensure_size0(avro_raw_array_t *array, size_t desired_count) +{ + int rval; + size_t old_allocated_size = array->allocated_size; + check(rval, avro_raw_array_ensure_size(array, desired_count)); + + if (array->allocated_size > old_allocated_size) { + size_t extra_space = array->allocated_size - old_allocated_size; + void *buf = array->data; + memset((char *)buf + old_allocated_size, 0, extra_space); + } + + return 0; +} + + +void *avro_raw_array_append(avro_raw_array_t *array) +{ + int rval; + + rval = avro_raw_array_ensure_size(array, array->element_count + 1); + if (rval) { + return NULL; + } + + size_t offset = array->element_size * array->element_count; + array->element_count++; + return (char *)array->data + offset; +} diff --git a/fluent-bit/lib/avro/src/avro-c.pc.in b/fluent-bit/lib/avro/src/avro-c.pc.in new file mode 100644 index 000000000..013afe4d1 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro-c.pc.in @@ -0,0 +1,7 @@ +Name: avro-c +Description: C library for parsing Avro data +Version: @VERSION@ +URL: https://avro.apache.org/ +Libs: -L@prefix@/lib -lavro +Cflags: -I@prefix@/include +Requires: @CODEC_PKG@ diff --git a/fluent-bit/lib/avro/src/avro.h b/fluent-bit/lib/avro/src/avro.h new file mode 100644 index 000000000..af32a32db --- /dev/null +++ b/fluent-bit/lib/avro/src/avro.h @@ -0,0 +1,40 @@ +/* + * 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. + */ +#ifndef AVRO_H +#define AVRO_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/allocation.h> +#include <avro/basics.h> +#include <avro/consumer.h> +#include <avro/data.h> +#include <avro/errors.h> +#include <avro/generic.h> +#include <avro/io.h> +#include <avro/legacy.h> +#include <avro/platform.h> +#include <avro/resolver.h> +#include <avro/schema.h> +#include <avro/value.h> + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/allocation.h b/fluent-bit/lib/avro/src/avro/allocation.h new file mode 100644 index 000000000..0ed412b94 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/allocation.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef AVRO_ALLOCATION_H +#define AVRO_ALLOCATION_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <stdlib.h> + +/* + * Allocation interface. You can provide a custom allocator for the + * library, should you wish. The allocator is provided as a single + * generic function, which can emulate the standard malloc, realloc, and + * free functions. The design of this allocation interface is inspired + * by the implementation of the Lua interpreter. + * + * The ptr parameter will be the location of any existing memory + * buffer. The osize parameter will be the size of this existing + * buffer. If ptr is NULL, then osize will be 0. The nsize parameter + * will be the size of the new buffer, or 0 if the new buffer should be + * freed. + * + * If nsize is 0, then the allocation function must return NULL. If + * nsize is not 0, then it should return NULL if the allocation fails. + */ + +typedef void * +(*avro_allocator_t)(void *user_data, void *ptr, size_t osize, size_t nsize); + +void avro_set_allocator(avro_allocator_t alloc, void *user_data); + +struct avro_allocator_state { + avro_allocator_t alloc; + void *user_data; +}; + +extern struct avro_allocator_state AVRO_CURRENT_ALLOCATOR; + +#define avro_realloc(ptr, osz, nsz) \ + (AVRO_CURRENT_ALLOCATOR.alloc \ + (AVRO_CURRENT_ALLOCATOR.user_data, \ + (ptr), (osz), (nsz))) + +#define avro_malloc(sz) (avro_realloc(NULL, 0, (sz))) +#define avro_free(ptr, osz) (avro_realloc((ptr), (osz), 0)) + +#define avro_new(type) (avro_realloc(NULL, 0, sizeof(type))) +#define avro_freet(type, ptr) (avro_realloc((ptr), sizeof(type), 0)) + +void *avro_calloc(size_t count, size_t size); + +/* + * This is probably too clever for our own good, but when we duplicate a + * string, we actually store its size in the same allocated memory + * buffer. That lets us free the string later, without having to call + * strlen to get its size, and without the containing struct having to + * manually store the strings length. + * + * This means that any string return by avro_strdup MUST be freed using + * avro_str_free, and the only thing that can be passed into + * avro_str_free is a string created via avro_strdup. + */ + +char *avro_str_alloc(size_t str_size); +char *avro_strdup(const char *str); +char *avro_strndup(const char *str, size_t size); +void avro_str_free(char *str); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/basics.h b/fluent-bit/lib/avro/src/avro/basics.h new file mode 100644 index 000000000..368509b90 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/basics.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef AVRO_BASICS_H +#define AVRO_BASICS_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + + +enum avro_type_t { + AVRO_STRING, + AVRO_BYTES, + AVRO_INT32, + AVRO_INT64, + AVRO_FLOAT, + AVRO_DOUBLE, + AVRO_BOOLEAN, + AVRO_NULL, + AVRO_RECORD, + AVRO_ENUM, + AVRO_FIXED, + AVRO_MAP, + AVRO_ARRAY, + AVRO_UNION, + AVRO_LINK +}; +typedef enum avro_type_t avro_type_t; + +enum avro_class_t { + AVRO_SCHEMA, + AVRO_DATUM +}; +typedef enum avro_class_t avro_class_t; + +struct avro_obj_t { + avro_type_t type; + avro_class_t class_type; + volatile int refcount; +}; + +#define avro_classof(obj) ((obj)->class_type) +#define is_avro_schema(obj) (obj && avro_classof(obj) == AVRO_SCHEMA) +#define is_avro_datum(obj) (obj && avro_classof(obj) == AVRO_DATUM) + +#define avro_typeof(obj) ((obj)->type) +#define is_avro_string(obj) (obj && avro_typeof(obj) == AVRO_STRING) +#define is_avro_bytes(obj) (obj && avro_typeof(obj) == AVRO_BYTES) +#define is_avro_int32(obj) (obj && avro_typeof(obj) == AVRO_INT32) +#define is_avro_int64(obj) (obj && avro_typeof(obj) == AVRO_INT64) +#define is_avro_float(obj) (obj && avro_typeof(obj) == AVRO_FLOAT) +#define is_avro_double(obj) (obj && avro_typeof(obj) == AVRO_DOUBLE) +#define is_avro_boolean(obj) (obj && avro_typeof(obj) == AVRO_BOOLEAN) +#define is_avro_null(obj) (obj && avro_typeof(obj) == AVRO_NULL) +#define is_avro_primitive(obj)(is_avro_string(obj) \ + ||is_avro_bytes(obj) \ + ||is_avro_int32(obj) \ + ||is_avro_int64(obj) \ + ||is_avro_float(obj) \ + ||is_avro_double(obj) \ + ||is_avro_boolean(obj) \ + ||is_avro_null(obj)) +#define is_avro_record(obj) (obj && avro_typeof(obj) == AVRO_RECORD) +#define is_avro_enum(obj) (obj && avro_typeof(obj) == AVRO_ENUM) +#define is_avro_fixed(obj) (obj && avro_typeof(obj) == AVRO_FIXED) +#define is_avro_named_type(obj)(is_avro_record(obj) \ + ||is_avro_enum(obj) \ + ||is_avro_fixed(obj)) +#define is_avro_map(obj) (obj && avro_typeof(obj) == AVRO_MAP) +#define is_avro_array(obj) (obj && avro_typeof(obj) == AVRO_ARRAY) +#define is_avro_union(obj) (obj && avro_typeof(obj) == AVRO_UNION) +#define is_avro_complex_type(obj) (!(is_avro_primitive(obj)) +#define is_avro_link(obj) (obj && avro_typeof(obj) == AVRO_LINK) + + + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/consumer.h b/fluent-bit/lib/avro/src/avro/consumer.h new file mode 100644 index 000000000..4128eef1a --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/consumer.h @@ -0,0 +1,317 @@ +/* + * 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. + */ + +#ifndef AVRO_CONSUMER_H +#define AVRO_CONSUMER_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> +#include <stdlib.h> + +#include <avro/io.h> +#include <avro/schema.h> + + +/*--------------------------------------------------------------------- + * Consumers + */ + +/** + * A <i>consumer</i> is an object that knows how to process Avro data. + * There are consumer methods for each type of Avro data. The + * <code>avro_consumer_t</code> struct is an abstract superclass, which + * you don't instantiate directly. Later in this file, we define + * several consumer classes that know how to process Avro data in + * specific ways. + * + * For compound Avro values (records, arrays, maps, and unions), the + * consumer callbacks provide a nested consumer that should be used to + * process subvalues. Each consumer instance, including these + * "subconsumers", contains a reference to the schema of the data that + * it expects to process. This means that the functions that produce + * Avro data (such as avro_consume_binary) don't need to maintain their + * own references to any schemas, since they'll be encapsulated in the + * consumer that they pass their data off to. + */ + +typedef struct avro_consumer_t avro_consumer_t; + +struct avro_consumer_t { + /** + * The schema of the data that this consumer expects to process. + */ + + avro_schema_t schema; + + /** + * Called when this consumer is freed. This function should + * free any additional resources acquired by a consumer + * subclass. + */ + + void (*free)(avro_consumer_t *consumer); + + /* PRIMITIVE VALUES */ + + /** + * Called when a boolean value is encountered. + */ + + int (*boolean_value)(avro_consumer_t *consumer, + int value, + void *user_data); + + /** + * Called when a bytes value is encountered. The @ref value + * pointer is only guaranteed to be valid for the duration of + * the callback function. If you need to save the data for + * processing later, you must copy it into another buffer. + */ + + int (*bytes_value)(avro_consumer_t *consumer, + const void *value, size_t value_len, + void *user_data); + + /** + * Called when a double value is encountered. + */ + + int (*double_value)(avro_consumer_t *consumer, + double value, + void *user_data); + + /** + * Called when a float value is encountered. + */ + + int (*float_value)(avro_consumer_t *consumer, + float value, + void *user_data); + + /** + * Called when an int value is encountered. + */ + + int (*int_value)(avro_consumer_t *consumer, + int32_t value, + void *user_data); + + /** + * Called when a long value is encountered. + */ + + int (*long_value)(avro_consumer_t *consumer, + int64_t value, + void *user_data); + + /** + * Called when a null value is encountered. + */ + + int (*null_value)(avro_consumer_t *consumer, void *user_data); + + /** + * Called when a string value is encountered. The @ref value + * pointer will point at UTF-8 encoded data. (If the data + * you're representing isn't a UTF-8 Unicode string, you + * should use the bytes type.) The @ref value_len parameter + * gives the length of the data in bytes, not in Unicode + * characters. The @ref value pointer is only guaranteed to + * be valid for the duration of the callback function. If you + * need to save the data for processing later, you must copy + * it into another buffer. + */ + + int (*string_value)(avro_consumer_t *consumer, + const void *value, size_t value_len, + void *user_data); + + /* COMPOUND VALUES */ + + /** + * Called when the beginning of an array block is encountered. + * The @ref block_count parameter will contain the number of + * elements in this block. + */ + + int (*array_start_block)(avro_consumer_t *consumer, + int is_first_block, + unsigned int block_count, + void *user_data); + + /** + * Called before each individual element of an array is + * processed. The index of the current element is passed into + * the callback. The callback should fill in @ref + * element_consumer and @ref element_user_data with the consumer + * and <code>user_data</code> pointer to use to process the + * element. + */ + + int (*array_element)(avro_consumer_t *consumer, + unsigned int index, + avro_consumer_t **element_consumer, + void **element_user_data, + void *user_data); + + /** + * Called when an enum value is encountered. + */ + + int (*enum_value)(avro_consumer_t *consumer, int value, + void *user_data); + + /** + * Called when a fixed value is encountered. The @ref value + * pointer is only guaranteed to be valid for the duration of + * the callback function. If you need to save the data for + * processing later, you must copy it into another buffer. + */ + + int (*fixed_value)(avro_consumer_t *consumer, + const void *value, size_t value_len, + void *user_data); + + /** + * Called when the beginning of a map block is encountered. + * The @ref block_count parameter will contain the number of + * elements in this block. + */ + + int (*map_start_block)(avro_consumer_t *consumer, + int is_first_block, + unsigned int block_count, + void *user_data); + + /** + * Called before each individual element of a map is + * processed. The index and key of the current element is + * passed into the callback. The key is only guaranteed to be + * valid for the duration of the map_element_start callback, + * and the map's subschema callback. If you need to save it for + * later use, you must copy the key into another memory + * location. The callback should fill in @ref value_consumer + * and @ref value_user_data with the consumer and + * <code>user_data</code> pointer to use to process the value. + */ + + int (*map_element)(avro_consumer_t *consumer, + unsigned int index, + const char *key, + avro_consumer_t **value_consumer, + void **value_user_data, + void *user_data); + + /** + * Called when the beginning of a record is encountered. + */ + + int (*record_start)(avro_consumer_t *consumer, + void *user_data); + + /** + * Called before each individual field of a record is + * processed. The index and name of the current field is + * passed into the callback. The name is only guaranteed to + * be valid for the duration of the record_field_start + * callback, and the field's subschema callback. If you need to + * save it for later use, you must copy the key into another + * memory location. The callback should fill in @ref + * field_consumer and @ref field_user_data with the consumer + * <code>user_data</code> pointer to use to process the field. + */ + + int (*record_field)(avro_consumer_t *consumer, + unsigned int index, + avro_consumer_t **field_consumer, + void **field_user_data, + void *user_data); + + /** + * Called when a union value is encountered. The callback + * should fill in @ref branch_consumer and @ref branch_user_data + * with the consumer <code>user_data</code> pointer to use to + * process the branch. + */ + + int (*union_branch)(avro_consumer_t *consumer, + unsigned int discriminant, + avro_consumer_t **branch_consumer, + void **branch_user_data, + void *user_data); +}; + + +/** + * Calls the given callback in consumer, if it's present. If the + * callback is NULL, it just returns a success code. + */ + +#define avro_consumer_call(consumer, callback, ...) \ + (((consumer)->callback == NULL)? 0: \ + (consumer)->callback((consumer), __VA_ARGS__)) + + +/** + * Frees an @ref avro_consumer_t instance. (This function works on + * consumer subclasses, too.) + */ + +void avro_consumer_free(avro_consumer_t *consumer); + + +/*--------------------------------------------------------------------- + * Resolvers + */ + +/** + * A <i>resolver</i> is a special kind of consumer that knows how to + * implement Avro's schema resolution rules to translate between a + * writer schema and a reader schema. The consumer callbacks line up + * with the writer schema; as each element of the datum is produced, the + * resolver fills in the contents of an @ref avro_datum_t instance. + * (The datum is provided as the user_data when you use the consumer.) + */ + +avro_consumer_t * +avro_resolver_new(avro_schema_t writer_schema, + avro_schema_t reader_schema); + + +/*--------------------------------------------------------------------- + * Binary encoding + */ + +/** + * Reads an Avro datum from the given @ref avro_reader_t. As the + * datum is read, each portion of it is passed off to the appropriate + * callback in @ref consumer. + */ + +int +avro_consume_binary(avro_reader_t reader, + avro_consumer_t *consumer, + void *ud); + + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/data.h b/fluent-bit/lib/avro/src/avro/data.h new file mode 100644 index 000000000..5b1d429b3 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/data.h @@ -0,0 +1,526 @@ +/* + * 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. + */ + +#ifndef AVRO_DATA_H +#define AVRO_DATA_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <stdlib.h> +#include <string.h> + +/* + * This file defines some helper data structures that are used within + * Avro, and in the schema-specific types created by avrocc. + */ + + +/*--------------------------------------------------------------------- + * Arrays + */ + +/** + * A resizeable array of fixed-size elements. + */ + +typedef struct avro_raw_array { + size_t element_size; + size_t element_count; + size_t allocated_size; + void *data; +} avro_raw_array_t; + +/** + * Initializes a new avro_raw_array_t that you've allocated yourself. + */ + +void avro_raw_array_init(avro_raw_array_t *array, size_t element_size); + +/** + * Finalizes an avro_raw_array_t. + */ + +void avro_raw_array_done(avro_raw_array_t *array); + +/** + * Clears an avro_raw_array_t. This does not deallocate any space; this + * allows us to reuse the underlying array buffer as we start to re-add + * elements to the array. + */ + +void avro_raw_array_clear(avro_raw_array_t *array); + +/** + * Ensures that there is enough allocated space to store the given + * number of elements in an avro_raw_array_t. If we can't allocate that + * much space, we return ENOMEM. + */ + +int +avro_raw_array_ensure_size(avro_raw_array_t *array, size_t desired_count); + +/** + * Ensures that there is enough allocated space to store the given + * number of elements in an avro_raw_array_t. If the array grows as a + * result of this operation, we will fill in any newly allocated space + * with 0 bytes. If we can't allocate that much space, we return + * ENOMEM. + */ + +int +avro_raw_array_ensure_size0(avro_raw_array_t *array, size_t desired_count); + +/** + * Returns the number of elements in an avro_raw_array_t. + */ + +#define avro_raw_array_size(array) ((array)->element_count) + +/** + * Returns the given element of an avro_raw_array_t as a <code>void + * *</code>. + */ + +#define avro_raw_array_get_raw(array, index) \ + ((char *) (array)->data + (array)->element_size * index) + +/** + * Returns the given element of an avro_raw_array_t, using element_type + * as the type of the elements. The result is *not* a pointer to the + * element; you get the element itself. + */ + +#define avro_raw_array_get(array, element_type, index) \ + (((element_type *) (array)->data)[index]) + +/** + * Appends a new element to an avro_raw_array_t, expanding it if + * necessary. Returns a pointer to the new element, or NULL if we + * needed to reallocate the array and couldn't. + */ + +void *avro_raw_array_append(avro_raw_array_t *array); + + +/*--------------------------------------------------------------------- + * Maps + */ + +/** + * The type of the elements in a map's elements array. + */ + +typedef struct avro_raw_map_entry { + const char *key; +} avro_raw_map_entry_t; + +/** + * A string-indexed map of fixed-size elements. + */ + +typedef struct avro_raw_map { + avro_raw_array_t elements; + void *indices_by_key; +} avro_raw_map_t; + +/** + * Initializes a new avro_raw_map_t that you've allocated yourself. + */ + +void avro_raw_map_init(avro_raw_map_t *map, size_t element_size); + +/** + * Finalizes an avro_raw_map_t. + */ + +void avro_raw_map_done(avro_raw_map_t *map); + +/** + * Clears an avro_raw_map_t. + */ + +void avro_raw_map_clear(avro_raw_map_t *map); + +/** + * Ensures that there is enough allocated space to store the given + * number of elements in an avro_raw_map_t. If we can't allocate that + * much space, we return ENOMEM. + */ + +int +avro_raw_map_ensure_size(avro_raw_map_t *map, size_t desired_count); + +/** + * Returns the number of elements in an avro_raw_map_t. + */ + +#define avro_raw_map_size(map) avro_raw_array_size(&((map)->elements)) + +/** + * Returns the avro_raw_map_entry_t for a given index. + */ + +#define avro_raw_get_entry(map, index) \ + ((avro_raw_map_entry_t *) \ + avro_raw_array_get_raw(&(map)->elements, index)) + +/** + * Returns the given element of an avro_raw_array_t as a <code>void + * *</code>. The indexes are assigned based on the order that the + * elements are added to the map. + */ + +#define avro_raw_map_get_raw(map, index) \ + (avro_raw_array_get_raw(&(map)->elements, index) + \ + sizeof(avro_raw_map_entry_t)) + +/** + * Returns the element of an avro_raw_map_t with the given numeric + * index. The indexes are assigned based on the order that the elements + * are added to the map. + */ + +#define avro_raw_map_get_by_index(map, element_type, index) \ + (*((element_type *) avro_raw_map_get_raw(map, index))) + +/** + * Returns the key of the element with the given numeric index. + */ + +#define avro_raw_map_get_key(map, index) \ + (avro_raw_get_entry(map, index)->key) + +/** + * Returns the element of an avro_raw_map_t with the given string key. + * If the given element doesn't exist, returns NULL. If @ref index + * isn't NULL, it will be filled in with the index of the element. + */ + +void *avro_raw_map_get(const avro_raw_map_t *map, const char *key, + size_t *index); + +/** + * Retrieves the element of an avro_raw_map_t with the given string key, + * creating it if necessary. A pointer to the element is placed into + * @ref element. If @ref index isn't NULL, it will be filled in with + * the index of the element. We return 1 if the element is new; 0 if + * it's not, and a negative error code if there was some problem. + */ + +int avro_raw_map_get_or_create(avro_raw_map_t *map, const char *key, + void **element, size_t *index); + + +/*--------------------------------------------------------------------- + * Wrapped buffers + */ + +/** + * A pointer to an unmodifiable external memory region, along with + * functions for freeing that buffer when it's no longer needed, and + * copying it. + */ + +typedef struct avro_wrapped_buffer avro_wrapped_buffer_t; + +struct avro_wrapped_buffer { + /** A pointer to the memory region */ + const void *buf; + + /** The size of the memory region */ + size_t size; + + /** Additional data needed by the methods below */ + void *user_data; + + /** + * A function that will be called when the memory region is no + * longer needed. This pointer can be NULL if nothing special + * needs to be done to free the buffer. + */ + void + (*free)(avro_wrapped_buffer_t *self); + + /** + * A function that makes a copy of a portion of a wrapped + * buffer. This doesn't have to involve duplicating the memory + * region, but it should ensure that the free method can be + * safely called on both copies without producing any errors or + * memory corruption. If this function is NULL, then we'll use + * a default implementation that calls @ref + * avro_wrapped_buffer_new_copy. + */ + int + (*copy)(avro_wrapped_buffer_t *dest, const avro_wrapped_buffer_t *src, + size_t offset, size_t length); + + /** + * A function that "slices" a wrapped buffer, causing it to + * point at a subset of the existing buffer. Usually, this just + * requires * updating the @ref buf and @ref size fields. If + * you don't need to do anything other than this, this function + * pointer can be left @c NULL. The function can assume that + * the @a offset and @a length parameters point to a valid + * subset of the existing wrapped buffer. + */ + int + (*slice)(avro_wrapped_buffer_t *self, size_t offset, size_t length); +}; + +/** + * Free a wrapped buffer. + */ + +#define avro_wrapped_buffer_free(self) \ + do { \ + if ((self)->free != NULL) { \ + (self)->free((self)); \ + } \ + } while (0) + +/** + * A static initializer for an empty wrapped buffer. + */ + +#define AVRO_WRAPPED_BUFFER_EMPTY { NULL, 0, NULL, NULL, NULL, NULL } + +/** + * Moves a wrapped buffer. After returning, @a dest will wrap the + * buffer that @a src used to point at, and @a src will be empty. + */ + +void +avro_wrapped_buffer_move(avro_wrapped_buffer_t *dest, + avro_wrapped_buffer_t *src); + +/** + * Copies a buffer. + */ + +int +avro_wrapped_buffer_copy(avro_wrapped_buffer_t *dest, + const avro_wrapped_buffer_t *src, + size_t offset, size_t length); + +/** + * Slices a buffer. + */ + +int +avro_wrapped_buffer_slice(avro_wrapped_buffer_t *self, + size_t offset, size_t length); + +/** + * Creates a new wrapped buffer wrapping the given memory region. You + * have to ensure that buf stays around for as long as you need to new + * wrapped buffer. If you copy the wrapped buffer (using + * avro_wrapped_buffer_copy), this will create a copy of the data. + * Additional copies will reuse this new copy. + */ + +int +avro_wrapped_buffer_new(avro_wrapped_buffer_t *dest, + const void *buf, size_t length); + +/** + * Creates a new wrapped buffer wrapping the given C string. + */ + +#define avro_wrapped_buffer_new_string(dest, str) \ + (avro_wrapped_buffer_new((dest), (str), strlen((str))+1)) + +/** + * Creates a new wrapped buffer containing a copy of the given memory + * region. This new copy will be reference counted; if you copy it + * further (using avro_wrapped_buffer_copy), the new copies will share a + * single underlying buffer. + */ + +int +avro_wrapped_buffer_new_copy(avro_wrapped_buffer_t *dest, + const void *buf, size_t length); + +/** + * Creates a new wrapped buffer containing a copy of the given C string. + */ + +#define avro_wrapped_buffer_new_string_copy(dest, str) \ + (avro_wrapped_buffer_new_copy((dest), (str), strlen((str))+1)) + + +/*--------------------------------------------------------------------- + * Strings + */ + +/** + * A resizable buffer for storing strings and bytes values. + */ + +typedef struct avro_raw_string { + avro_wrapped_buffer_t wrapped; +} avro_raw_string_t; + +/** + * Initializes an avro_raw_string_t that you've allocated yourself. + */ + +void avro_raw_string_init(avro_raw_string_t *str); + +/** + * Finalizes an avro_raw_string_t. + */ + +void avro_raw_string_done(avro_raw_string_t *str); + +/** + * Returns the length of the data stored in an avro_raw_string_t. If + * the buffer contains a C string, this length includes the NUL + * terminator. + */ + +#define avro_raw_string_length(str) ((str)->wrapped.size) + +/** + * Returns a pointer to the data stored in an avro_raw_string_t. + */ + +#define avro_raw_string_get(str) ((str)->wrapped.buf) + +/** + * Fills an avro_raw_string_t with a copy of the given buffer. + */ + +void avro_raw_string_set_length(avro_raw_string_t *str, + const void *src, + size_t length); + +/** + * Fills an avro_raw_string_t with a copy of the given C string. + */ + +void avro_raw_string_set(avro_raw_string_t *str, const char *src); + +/** + * Appends the given C string to an avro_raw_string_t. + */ + +void avro_raw_string_append(avro_raw_string_t *str, const char *src); + +/** + * Appends the given C string to an avro_raw_string_t, using the + * provided length instead of calling strlen(src). + */ + +void avro_raw_string_append_length(avro_raw_string_t *str, + const void *src, + size_t length); +/** + * Gives control of a buffer to an avro_raw_string_t. + */ + +void +avro_raw_string_give(avro_raw_string_t *str, + avro_wrapped_buffer_t *src); + +/** + * Returns an avro_wrapped_buffer_t for the content of the string, + * ideally without copying it. + */ + +int +avro_raw_string_grab(const avro_raw_string_t *str, + avro_wrapped_buffer_t *dest); + +/** + * Clears an avro_raw_string_t. + */ + +void avro_raw_string_clear(avro_raw_string_t *str); + + +/** + * Tests two avro_raw_string_t instances for equality. + */ + +int avro_raw_string_equals(const avro_raw_string_t *str1, + const avro_raw_string_t *str2); + + +/*--------------------------------------------------------------------- + * Memoization + */ + +/** + * A specialized map that can be used to memoize the results of a + * function. The API allows you to use two keys as the memoization + * keys; if you only need one key, just use NULL for the second key. + * The result of the function should be a single pointer, or an integer + * that can be cast into a pointer (i.e., an intptr_t). + */ + +typedef struct avro_memoize { + void *cache; +} avro_memoize_t; + +/** + * Initialize an avro_memoize_t that you've allocated for yourself. + */ + +void +avro_memoize_init(avro_memoize_t *mem); + +/** + * Finalizes an avro_memoize_t. + */ + +void +avro_memoize_done(avro_memoize_t *mem); + +/** + * Search for a cached value in an avro_memoize_t. Returns a boolean + * indicating whether there's a value in the cache for the given keys. + * If there is, the cached result is placed into @ref result. + */ + +int +avro_memoize_get(avro_memoize_t *mem, + void *key1, void *key2, + void **result); + +/** + * Stores a new cached value into an avro_memoize_t, overwriting it if + * necessary. + */ + +void +avro_memoize_set(avro_memoize_t *mem, + void *key1, void *key2, + void *result); + +/** + * Removes any cached value for the given key from an avro_memoize_t. + */ + +void +avro_memoize_delete(avro_memoize_t *mem, void *key1, void *key2); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/errors.h b/fluent-bit/lib/avro/src/avro/errors.h new file mode 100644 index 000000000..c7991344b --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/errors.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef AVRO_ERRORS_H +#define AVRO_ERRORS_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +/* + * Returns a textual description of the last error condition returned by + * an Avro function. + */ + +const char *avro_strerror(void); + +void +avro_set_error(const char *fmt, ...); + +void +avro_prefix_error(const char *fmt, ...); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/generic.h b/fluent-bit/lib/avro/src/avro/generic.h new file mode 100644 index 000000000..5e6047fb7 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/generic.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef AVRO_GENERIC_H +#define AVRO_GENERIC_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> +#include <stdlib.h> + +#include <avro/schema.h> +#include <avro/value.h> + +/* + * This file contains an avro_value_t implementation that can store + * values of any Avro schema. It replaces the old avro_datum_t class. + */ + + +/** + * Return a generic avro_value_iface_t implementation for the given + * schema, regardless of what type it is. + */ + +avro_value_iface_t * +avro_generic_class_from_schema(avro_schema_t schema); + +/** + * Allocate a new instance of the given generic value class. @a iface + * must have been created by @ref avro_generic_class_from_schema. + */ + +int +avro_generic_value_new(avro_value_iface_t *iface, avro_value_t *dest); + + +/* + * These functions return an avro_value_iface_t implementation for each + * primitive schema type. (For enum, fixed, and the compound types, you + * must use the @ref avro_generic_class_from_schema function.) + */ + +avro_value_iface_t *avro_generic_boolean_class(void); +avro_value_iface_t *avro_generic_bytes_class(void); +avro_value_iface_t *avro_generic_double_class(void); +avro_value_iface_t *avro_generic_float_class(void); +avro_value_iface_t *avro_generic_int_class(void); +avro_value_iface_t *avro_generic_long_class(void); +avro_value_iface_t *avro_generic_null_class(void); +avro_value_iface_t *avro_generic_string_class(void); + + +/* + * These functions instantiate a new generic primitive value. + */ + +int avro_generic_boolean_new(avro_value_t *value, int val); +int avro_generic_bytes_new(avro_value_t *value, void *buf, size_t size); +int avro_generic_double_new(avro_value_t *value, double val); +int avro_generic_float_new(avro_value_t *value, float val); +int avro_generic_int_new(avro_value_t *value, int32_t val); +int avro_generic_long_new(avro_value_t *value, int64_t val); +int avro_generic_null_new(avro_value_t *value); +int avro_generic_string_new(avro_value_t *value, const char *val); +int avro_generic_string_new_length(avro_value_t *value, const char *val, size_t size); + + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/io.h b/fluent-bit/lib/avro/src/avro/io.h new file mode 100644 index 000000000..ffbb68dc5 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/io.h @@ -0,0 +1,156 @@ +/* + * 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. + */ + +#ifndef AVRO_IO_H +#define AVRO_IO_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> +#include <stdio.h> + +#include <avro/basics.h> +#include <avro/legacy.h> +#include <avro/schema.h> +#include <avro/value.h> + +typedef struct avro_reader_t_ *avro_reader_t; +typedef struct avro_writer_t_ *avro_writer_t; + +/* + * io + */ + +avro_reader_t avro_reader_file(FILE * fp); +avro_reader_t avro_reader_file_fp(FILE * fp, int should_close); +avro_writer_t avro_writer_file(FILE * fp); +avro_writer_t avro_writer_file_fp(FILE * fp, int should_close); +avro_reader_t avro_reader_memory(const char *buf, int64_t len); +avro_writer_t avro_writer_memory(const char *buf, int64_t len); + +void +avro_reader_memory_set_source(avro_reader_t reader, const char *buf, int64_t len); + +void +avro_writer_memory_set_dest(avro_writer_t writer, const char *buf, int64_t len); + +int avro_read(avro_reader_t reader, void *buf, int64_t len); +int avro_skip(avro_reader_t reader, int64_t len); +int avro_write(avro_writer_t writer, void *buf, int64_t len); + +void avro_reader_reset(avro_reader_t reader); + +void avro_writer_reset(avro_writer_t writer); +int64_t avro_writer_tell(avro_writer_t writer); +void avro_writer_flush(avro_writer_t writer); + +void avro_writer_dump(avro_writer_t writer, FILE * fp); +void avro_reader_dump(avro_reader_t reader, FILE * fp); + +int avro_reader_is_eof(avro_reader_t reader); + +void avro_reader_free(avro_reader_t reader); +void avro_writer_free(avro_writer_t writer); + +int avro_schema_to_json(const avro_schema_t schema, avro_writer_t out); + +/* + * Reads a binary-encoded Avro value from the given reader object, + * storing the result into dest. + */ + +int +avro_value_read(avro_reader_t reader, avro_value_t *dest); + +/* + * Writes a binary-encoded Avro value to the given writer object. + */ + +int +avro_value_write(avro_writer_t writer, avro_value_t *src); + +/* + * Returns the size of the binary encoding of the given Avro value. + */ + +int +avro_value_sizeof(avro_value_t *src, size_t *size); + + +/* File object container */ +typedef struct avro_file_reader_t_ *avro_file_reader_t; +typedef struct avro_file_writer_t_ *avro_file_writer_t; + +int avro_file_writer_create(const char *path, avro_schema_t schema, + avro_file_writer_t * writer); +int avro_file_writer_create_fp(FILE *fp, const char *path, int should_close, + avro_schema_t schema, avro_file_writer_t * writer); +int avro_file_writer_create_with_codec(const char *path, + avro_schema_t schema, avro_file_writer_t * writer, + const char *codec, size_t block_size); +int avro_file_writer_create_with_codec_fp(FILE *fp, const char *path, int should_close, + avro_schema_t schema, avro_file_writer_t * writer, + const char *codec, size_t block_size); +int avro_file_writer_open(const char *path, avro_file_writer_t * writer); +int avro_file_writer_open_bs(const char *path, avro_file_writer_t * writer, size_t block_size); +int avro_file_reader(const char *path, avro_file_reader_t * reader); +int avro_file_reader_fp(FILE *fp, const char *path, int should_close, + avro_file_reader_t * reader); + +avro_schema_t +avro_file_reader_get_writer_schema(avro_file_reader_t reader); + +int avro_file_writer_sync(avro_file_writer_t writer); +int avro_file_writer_flush(avro_file_writer_t writer); +int avro_file_writer_close(avro_file_writer_t writer); + +int avro_file_reader_close(avro_file_reader_t reader); + +int +avro_file_reader_read_value(avro_file_reader_t reader, avro_value_t *dest); + +int +avro_file_writer_append_value(avro_file_writer_t writer, avro_value_t *src); + +int +avro_file_writer_append_encoded(avro_file_writer_t writer, + const void *buf, int64_t len); + +/* + * Legacy avro_datum_t API + */ + +int avro_read_data(avro_reader_t reader, + avro_schema_t writer_schema, + avro_schema_t reader_schema, avro_datum_t * datum); +int avro_skip_data(avro_reader_t reader, avro_schema_t writer_schema); +int avro_write_data(avro_writer_t writer, + avro_schema_t writer_schema, avro_datum_t datum); +int64_t avro_size_data(avro_writer_t writer, + avro_schema_t writer_schema, avro_datum_t datum); + +int avro_file_writer_append(avro_file_writer_t writer, avro_datum_t datum); + +int avro_file_reader_read(avro_file_reader_t reader, + avro_schema_t readers_schema, avro_datum_t * datum); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/legacy.h b/fluent-bit/lib/avro/src/avro/legacy.h new file mode 100644 index 000000000..ba8f29bab --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/legacy.h @@ -0,0 +1,264 @@ +/* + * 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. + */ + +#ifndef AVRO_LEGACY_H +#define AVRO_LEGACY_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> +#include <stdio.h> + +#include <avro/basics.h> +#include <avro/data.h> +#include <avro/schema.h> +#include <avro/value.h> + +/* + * This file defines the deprecated interface for handling Avro values. + * It's here solely for backwards compatibility. New code should use + * the avro_value_t interface (defined in avro/value.h). The + * avro_datum_t type has been replaced by the “generic” implementation + * of the value interface, which is defined in avro/generic.h. You can + * also use your own application-specific types as Avro values by + * defining your own avro_value_t implementation for them. + */ + +/** + * A function used to free a bytes, string, or fixed buffer once it is + * no longer needed by the datum that wraps it. + */ + +typedef void +(*avro_free_func_t)(void *ptr, size_t sz); + +/** + * An avro_free_func_t that frees the buffer using the custom allocator + * provided to avro_set_allocator. + */ + +void +avro_alloc_free_func(void *ptr, size_t sz); + +/* + * Datum constructors. Each datum stores a reference to the schema that + * the datum is an instance of. The primitive datum constructors don't + * need to take in an explicit avro_schema_t parameter, since there's + * only one schema that they could be an instance of. The complex + * constructors do need an explicit schema parameter. + */ + +typedef struct avro_obj_t *avro_datum_t; +avro_datum_t avro_string(const char *str); +avro_datum_t avro_givestring(const char *str, + avro_free_func_t free); +avro_datum_t avro_bytes(const char *buf, int64_t len); +avro_datum_t avro_givebytes(const char *buf, int64_t len, + avro_free_func_t free); +avro_datum_t avro_int32(int32_t i); +avro_datum_t avro_int64(int64_t l); +avro_datum_t avro_float(float f); +avro_datum_t avro_double(double d); +avro_datum_t avro_boolean(int8_t i); +avro_datum_t avro_null(void); +avro_datum_t avro_record(avro_schema_t schema); +avro_datum_t avro_enum(avro_schema_t schema, int i); +avro_datum_t avro_fixed(avro_schema_t schema, + const char *bytes, const int64_t size); +avro_datum_t avro_givefixed(avro_schema_t schema, + const char *bytes, const int64_t size, + avro_free_func_t free); +avro_datum_t avro_map(avro_schema_t schema); +avro_datum_t avro_array(avro_schema_t schema); +avro_datum_t avro_union(avro_schema_t schema, + int64_t discriminant, const avro_datum_t datum); + +/** + * Returns the schema that the datum is an instance of. + */ + +avro_schema_t avro_datum_get_schema(const avro_datum_t datum); + +/* + * Constructs a new avro_datum_t instance that's appropriate for holding + * values of the given schema. + */ + +avro_datum_t avro_datum_from_schema(const avro_schema_t schema); + +/* getters */ +int avro_string_get(avro_datum_t datum, char **p); +int avro_bytes_get(avro_datum_t datum, char **bytes, int64_t * size); +int avro_int32_get(avro_datum_t datum, int32_t * i); +int avro_int64_get(avro_datum_t datum, int64_t * l); +int avro_float_get(avro_datum_t datum, float *f); +int avro_double_get(avro_datum_t datum, double *d); +int avro_boolean_get(avro_datum_t datum, int8_t * i); + +int avro_enum_get(const avro_datum_t datum); +const char *avro_enum_get_name(const avro_datum_t datum); +int avro_fixed_get(avro_datum_t datum, char **bytes, int64_t * size); +int avro_record_get(const avro_datum_t record, const char *field_name, + avro_datum_t * value); + +/* + * A helper macro that extracts the value of the given field of a + * record. + */ + +#define avro_record_get_field_value(rc, rec, typ, fname, ...) \ + do { \ + avro_datum_t field = NULL; \ + (rc) = avro_record_get((rec), (fname), &field); \ + if (rc) break; \ + (rc) = avro_##typ##_get(field, __VA_ARGS__); \ + } while (0) + + +int avro_map_get(const avro_datum_t datum, const char *key, + avro_datum_t * value); +/* + * For maps, the "index" for each entry is based on the order that they + * were added to the map. + */ +int avro_map_get_key(const avro_datum_t datum, int index, + const char **key); +int avro_map_get_index(const avro_datum_t datum, const char *key, + int *index); +size_t avro_map_size(const avro_datum_t datum); +int avro_array_get(const avro_datum_t datum, int64_t index, avro_datum_t * value); +size_t avro_array_size(const avro_datum_t datum); + +/* + * These accessors allow you to query the current branch of a union + * value, returning either the branch's discriminant value or the + * avro_datum_t of the branch. A union value can be uninitialized, in + * which case the discriminant will be -1 and the datum NULL. + */ + +int64_t avro_union_discriminant(const avro_datum_t datum); +avro_datum_t avro_union_current_branch(avro_datum_t datum); + +/* setters */ +int avro_string_set(avro_datum_t datum, const char *p); +int avro_givestring_set(avro_datum_t datum, const char *p, + avro_free_func_t free); + +int avro_bytes_set(avro_datum_t datum, const char *bytes, const int64_t size); +int avro_givebytes_set(avro_datum_t datum, const char *bytes, + const int64_t size, + avro_free_func_t free); + +int avro_int32_set(avro_datum_t datum, const int32_t i); +int avro_int64_set(avro_datum_t datum, const int64_t l); +int avro_float_set(avro_datum_t datum, const float f); +int avro_double_set(avro_datum_t datum, const double d); +int avro_boolean_set(avro_datum_t datum, const int8_t i); + +int avro_enum_set(avro_datum_t datum, const int symbol_value); +int avro_enum_set_name(avro_datum_t datum, const char *symbol_name); +int avro_fixed_set(avro_datum_t datum, const char *bytes, const int64_t size); +int avro_givefixed_set(avro_datum_t datum, const char *bytes, + const int64_t size, + avro_free_func_t free); + +int avro_record_set(avro_datum_t record, const char *field_name, + avro_datum_t value); + +/* + * A helper macro that sets the value of the given field of a record. + */ + +#define avro_record_set_field_value(rc, rec, typ, fname, ...) \ + do { \ + avro_datum_t field = NULL; \ + (rc) = avro_record_get((rec), (fname), &field); \ + if (rc) break; \ + (rc) = avro_##typ##_set(field, __VA_ARGS__); \ + } while (0) + +int avro_map_set(avro_datum_t map, const char *key, + avro_datum_t value); +int avro_array_append_datum(avro_datum_t array_datum, + avro_datum_t datum); + +/* + * This function selects the active branch of a union value, and can be + * safely called on an existing union to change the current branch. If + * the branch changes, we'll automatically construct a new avro_datum_t + * for the new branch's schema type. If the desired branch is already + * the active branch of the union, we'll leave the existing datum + * instance as-is. The branch datum will be placed into the "branch" + * parameter, regardless of whether we have to create a new datum + * instance or not. + */ + +int avro_union_set_discriminant(avro_datum_t unionp, + int discriminant, + avro_datum_t *branch); + +/** + * Resets a datum instance. For arrays and maps, this frees all + * elements and clears the container. For records and unions, this + * recursively resets any child datum instances. + */ + +int +avro_datum_reset(avro_datum_t value); + +/* reference counting */ +avro_datum_t avro_datum_incref(avro_datum_t value); +void avro_datum_decref(avro_datum_t value); + +void avro_datum_print(avro_datum_t value, FILE * fp); + +int avro_datum_equal(avro_datum_t a, avro_datum_t b); + +/* + * Returns a string containing the JSON encoding of an Avro value. You + * must free this string when you're done with it, using the standard + * free() function. (*Not* using the custom Avro allocator.) + */ + +int avro_datum_to_json(const avro_datum_t datum, + int one_line, char **json_str); + + +int avro_schema_datum_validate(avro_schema_t + expected_schema, avro_datum_t datum); + +/* + * An avro_value_t implementation for avro_datum_t objects. + */ + +avro_value_iface_t * +avro_datum_class(void); + +/* + * Creates a new avro_value_t instance for the given datum. + */ + +int +avro_datum_as_value(avro_value_t *value, avro_datum_t src); + + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/msinttypes.h b/fluent-bit/lib/avro/src/avro/msinttypes.h new file mode 100644 index 000000000..29be14b95 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/msinttypes.h @@ -0,0 +1,315 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +////////////////////////////////////// +// Start AVRO specific modifications +////////////////////////////////////// +#include "msstdint.h" + +// Modification for AVRO of inttypes.h +#define __STDC_FORMAT_MACROS (1) + +////////////////////////////////////// +// End AVRO specific modifications +////////////////////////////////////// + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + + +#endif // _MSC_INTTYPES_H_ ] diff --git a/fluent-bit/lib/avro/src/avro/msstdint.h b/fluent-bit/lib/avro/src/avro/msstdint.h new file mode 100644 index 000000000..d02608a59 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/msstdint.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include <limits.h> + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include <wchar.h> +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/fluent-bit/lib/avro/src/avro/platform.h b/fluent-bit/lib/avro/src/avro/platform.h new file mode 100644 index 000000000..929305505 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/platform.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef AVRO_PLATFORM_H +#define AVRO_PLATFORM_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +/* Use this header file to include platform specific definitions */ + +#ifdef _WIN32 + #include <avro/msinttypes.h> +#else + #include <inttypes.h> +#endif + +// Defines for printing size_t. +#if defined(_WIN64) + #define PRIsz PRIu64 +#elif defined(_WIN32) + #define PRIsz PRIu32 +#else // GCC + #define PRIsz "zu" +#endif + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/refcount.h b/fluent-bit/lib/avro/src/avro/refcount.h new file mode 100644 index 000000000..27369900a --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/refcount.h @@ -0,0 +1,306 @@ +/* + * 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. + */ + +#ifndef AVRO_REFCOUNT_H +#define AVRO_REFCOUNT_H + +#if defined(_WIN32) && defined(__cplusplus) +/* Include the C++ file <intrin.h> outside the scope of extern "C" */ +#include <intrin.h> +#endif + +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +/** + * Atomically sets the value of a reference count. + */ + +static inline void +avro_refcount_set(volatile int *refcount, int value); + +/** + * Increments a reference count, ensuring that its value doesn't + * overflow. + */ + +static inline void +avro_refcount_inc(volatile int *refcount); + +/** + * Decrements a reference count, and returns whether the resulting + * (decremented) value is 0. + */ + +static inline int +avro_refcount_dec(volatile int *refcount); + + +/*----------------------------------------------------------------------- + * Non-Atomic Reference Count + */ +#if defined(AVRO_NON_ATOMIC_REFCOUNT) +static inline void +avro_refcount_set(volatile int *refcount, int value) +{ + *refcount = value; +} + +static inline void +avro_refcount_inc(volatile int *refcount) +{ + if (*refcount != (int) -1) { + *refcount += 1; + } +} + +static inline int +avro_refcount_dec(volatile int *refcount) +{ + if (*refcount != (int) -1) { + *refcount -= 1; + return (*refcount == 0); + } + return 0; +} + +/*----------------------------------------------------------------------- + * Mac OS X + */ + +#elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + +#include <libkern/OSAtomic.h> + +static inline void +avro_refcount_set(volatile int *refcount, int value) +{ + *refcount = value; +} + +static inline void +avro_refcount_inc(volatile int *refcount) +{ + if (*refcount != (int) -1) { + OSAtomicIncrement32(refcount); + } +} + +static inline int +avro_refcount_dec(volatile int *refcount) +{ + if (*refcount != (int) -1) { + return (OSAtomicDecrement32(refcount) == 0); + } + return 0; +} + + +/*----------------------------------------------------------------------- + * GCC intrinsics + */ + +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40500 \ +|| defined(__clang__) + +static inline void +avro_refcount_set(volatile int *refcount, int value) +{ + *refcount = value; +} + +static inline void +avro_refcount_inc(volatile int *refcount) +{ + if (*refcount != (int) -1) { + __sync_add_and_fetch(refcount, 1); + } +} + +static inline int +avro_refcount_dec(volatile int *refcount) +{ + if (*refcount != (int) -1) { + return (__sync_sub_and_fetch(refcount, 1) == 0); + } + return 0; +} + + +/*----------------------------------------------------------------------- + * Raw x86 assembly + */ + +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +/* determine the size of int */ + +#include <limits.h> +#include <avro/platform.h> +#if INT_MAX == INT32_MAX +#define REFCOUNT_SS "l" +#elif INT_MAX == INT64_MAX +#define REFCOUNT_SS "q" +#else +#error "Unknown int size" +#endif + +static inline void +avro_refcount_set(volatile int *refcount, int value) +{ + *refcount = value; +} + +static inline void +avro_refcount_inc(volatile int *refcount) +{ + if (*refcount != (int) -1) { + __asm__ __volatile__ ("lock ; inc"REFCOUNT_SS" %0" + :"=m" (*refcount) + :"m" (*refcount)); + } +} + +static inline int +avro_refcount_dec(volatile int *refcount) +{ + if (*refcount != (int) -1) { + char result; + __asm__ __volatile__ ("lock ; dec"REFCOUNT_SS" %0; setz %1" + :"=m" (*refcount), "=q" (result) + :"m" (*refcount)); + return result; + } + return 0; +} + +#undef REFCOUNT_SS + + +/*----------------------------------------------------------------------- + * Raw PPC assembly + */ + +#elif defined(__GNUC__) && defined(__ppc__) + +static inline int +avro_refcount_LL_int(volatile int *ptr) +{ + int val; + __asm__ __volatile__ ("lwarx %[val],0,%[ptr]" + : [val] "=r" (val) + : [ptr] "r" (&ptr) + : "cc"); + + return val; +} + +/* Returns non-zero if the store was successful, zero otherwise. */ +static inline int +avro_refcount_SC_int(volatile int *ptr, int val) +{ + int ret = 1; /* init to non-zero, will be reset to 0 if SC was successful */ + __asm__ __volatile__ ("stwcx. %[val],0,%[ptr];\n" + "beq 1f;\n" + "li %[ret], 0;\n" + "1: ;\n" + : [ret] "=r" (ret) + : [ptr] "r" (&ptr), [val] "r" (val), "0" (ret) + : "cc", "memory"); + return ret; +} + +static inline void +avro_refcount_set(volatile int *refcount, int value) +{ + *refcount = value; +} + +static inline void +avro_refcount_inc(volatile int *refcount) +{ + int prev; + do { + prev = avro_refcount_LL_int(refcount); + if (prev == (int) -1) { + return; + } + } while (!avro_refcount_SC_int(refcount, prev + 1)); +} + +static inline int +avro_refcount_dec(volatile int *refcount) +{ + int prev; + do { + prev = avro_refcount_LL_int(refcount); + if (prev == (int) -1) { + return 0; + } + } while (!avro_refcount_SC_int(refcount, prev - 1)); + return prev == 1; +} + + +/*----------------------------------------------------------------------- + * Windows intrinsics + */ +#elif defined(_WIN32) + +#ifdef __cplusplus +// Note: <intrin.h> included outside the extern "C" wrappers above +#else +#include <windows.h> +#include <intrin.h> +#endif // __cplusplus + +static inline void +avro_refcount_set(volatile int *refcount, int value) +{ + *refcount = value; +} + +static inline void +avro_refcount_inc(volatile int *refcount) +{ + if (*refcount != (int) -1) { + _InterlockedIncrement((volatile long *) refcount); + } +} + +static inline int +avro_refcount_dec(volatile int *refcount) +{ + if (*refcount != (int) -1) { + return (_InterlockedDecrement((volatile long *) refcount) == 0); + } + return 0; +} + +/*----------------------------------------------------------------------- + * Fallback + */ +#else +#error "No atomic implementation!" +#endif + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/resolver.h b/fluent-bit/lib/avro/src/avro/resolver.h new file mode 100644 index 000000000..472e0f95d --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/resolver.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#ifndef AVRO_RESOLVER_H +#define AVRO_RESOLVER_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/schema.h> +#include <avro/value.h> + +/* + * A <i>resolved value</i> is a special kind of value that knows how to + * implement Avro's schema resolution rules to translate between a + * writer schema and a reader schema. A resolved value doesn't store or + * process data itself; instead, it wraps an existing value instance. + * + * There are two resolved value classes. In the first (@ref + * avro_resolved_writer_t), the resolved value is an instance of the + * writer schema, and wraps an instance of the reader schema. This is + * used, for instance, when reading from an Avro data file; you want the + * end result to be a reader schema value, and the resolved value allows + * the file reader to ignore the schema resolution and simply fill in + * the values of the writer schema. You can only set the values of a + * resolved writer; you must use the original wrapped value to read. + * + * With other class (@ref avro_resolved_reader_t), the resolved value is + * an instance of the reader schema, and wraps an instance of the writer + * schema. This is used when resolving an existing Avro value to + * another schema; you've already got the value in the original (writer) + * schema, and want to transparently treat it as if it were an instance + * of the new (reader) schema. You can only read the values of a + * resolved reader; you must use the original wrapped value to write. + * + * For both classes, the “self” pointer of the resolved value is an + * avro_value_t pointer, which points at the wrapped value. + */ + + +/** + * Create a new resolved writer implementation for the given writer and + * reader schemas. + */ + +avro_value_iface_t * +avro_resolved_writer_new(avro_schema_t writer_schema, + avro_schema_t reader_schema); + +/** + * Creates a new resolved writer value. + */ + +int +avro_resolved_writer_new_value(avro_value_iface_t *iface, + avro_value_t *value); + +/** + * Sets the wrapped value for a resolved writer. This must be an + * instance of the reader schema. We create our own reference to the + * destination value. + */ + +void +avro_resolved_writer_set_dest(avro_value_t *resolved, + avro_value_t *dest); + + +/** + * Clears the wrapped value for a resolved writer. + */ + +void +avro_resolved_writer_clear_dest(avro_value_t *resolved); + + +/** + * Create a new resolved reader implementation for the given writer and + * reader schemas. + */ + +avro_value_iface_t * +avro_resolved_reader_new(avro_schema_t writer_schema, + avro_schema_t reader_schema); + +/** + * Creates a new resolved reader value. + */ + +int +avro_resolved_reader_new_value(avro_value_iface_t *iface, + avro_value_t *value); + +/** + * Sets the wrapped value for a resolved reader. This must be an + * instance of the reader schema. We create our own reference to the + * source value. + */ + +void +avro_resolved_reader_set_source(avro_value_t *resolved, + avro_value_t *dest); + + +/** + * Clears the wrapped value for a resolved reader. + */ + +void +avro_resolved_reader_clear_source(avro_value_t *resolved); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/schema.h b/fluent-bit/lib/avro/src/avro/schema.h new file mode 100644 index 000000000..51d456155 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/schema.h @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#ifndef AVRO_SCHEMA_H +#define AVRO_SCHEMA_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> +#include <stdlib.h> + +#include <avro/basics.h> + +typedef struct avro_obj_t *avro_schema_t; + +avro_schema_t avro_schema_string(void); +avro_schema_t avro_schema_bytes(void); +avro_schema_t avro_schema_int(void); +avro_schema_t avro_schema_long(void); +avro_schema_t avro_schema_float(void); +avro_schema_t avro_schema_double(void); +avro_schema_t avro_schema_boolean(void); +avro_schema_t avro_schema_null(void); + +avro_schema_t avro_schema_record(const char *name, const char *space); +avro_schema_t avro_schema_record_field_get(const avro_schema_t + record, const char *field_name); +const char *avro_schema_record_field_name(const avro_schema_t schema, int index); +int avro_schema_record_field_get_index(const avro_schema_t schema, + const char *field_name); +avro_schema_t avro_schema_record_field_get_by_index +(const avro_schema_t record, int index); +int avro_schema_record_field_append(const avro_schema_t record, + const char *field_name, + const avro_schema_t type); +size_t avro_schema_record_size(const avro_schema_t record); + +avro_schema_t avro_schema_enum(const char *name); +avro_schema_t avro_schema_enum_ns(const char *name, const char *space); +const char *avro_schema_enum_get(const avro_schema_t enump, + int index); +int avro_schema_enum_get_by_name(const avro_schema_t enump, + const char *symbol_name); +int avro_schema_enum_symbol_append(const avro_schema_t + enump, const char *symbol); +int avro_schema_enum_number_of_symbols(const avro_schema_t enump); + +avro_schema_t avro_schema_fixed(const char *name, const int64_t len); +avro_schema_t avro_schema_fixed_ns(const char *name, const char *space, + const int64_t len); +int64_t avro_schema_fixed_size(const avro_schema_t fixed); + +avro_schema_t avro_schema_map(const avro_schema_t values); +avro_schema_t avro_schema_map_values(avro_schema_t map); + +avro_schema_t avro_schema_array(const avro_schema_t items); +avro_schema_t avro_schema_array_items(avro_schema_t array); + +avro_schema_t avro_schema_union(void); +size_t avro_schema_union_size(const avro_schema_t union_schema); +int avro_schema_union_append(const avro_schema_t + union_schema, const avro_schema_t schema); +avro_schema_t avro_schema_union_branch(avro_schema_t union_schema, + int branch_index); +avro_schema_t avro_schema_union_branch_by_name +(avro_schema_t union_schema, int *branch_index, const char *name); + +avro_schema_t avro_schema_link(avro_schema_t schema); +avro_schema_t avro_schema_link_target(avro_schema_t schema); + +typedef struct avro_schema_error_t_ *avro_schema_error_t; + +int avro_schema_from_json(const char *jsontext, int32_t unused1, + avro_schema_t *schema, avro_schema_error_t *unused2); + +/* jsontext does not need to be NUL terminated. length must *NOT* + * include the NUL terminator, if one is present. */ +int avro_schema_from_json_length(const char *jsontext, size_t length, + avro_schema_t *schema); + +/* A helper macro for loading a schema from a string literal. The + * literal must be declared as a char[], not a char *, since we use the + * sizeof operator to determine its length. */ +#define avro_schema_from_json_literal(json, schema) \ + (avro_schema_from_json_length((json), sizeof((json))-1, (schema))) + +int avro_schema_to_specific(avro_schema_t schema, const char *prefix); + +avro_schema_t avro_schema_get_subschema(const avro_schema_t schema, + const char *name); +const char *avro_schema_name(const avro_schema_t schema); +const char *avro_schema_namespace(const avro_schema_t schema); +const char *avro_schema_type_name(const avro_schema_t schema); +avro_schema_t avro_schema_copy(avro_schema_t schema); +int avro_schema_equal(avro_schema_t a, avro_schema_t b); + +avro_schema_t avro_schema_incref(avro_schema_t schema); +int avro_schema_decref(avro_schema_t schema); + +int avro_schema_match(avro_schema_t writers_schema, + avro_schema_t readers_schema); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro/value.h b/fluent-bit/lib/avro/src/avro/value.h new file mode 100644 index 000000000..c5619e7bf --- /dev/null +++ b/fluent-bit/lib/avro/src/avro/value.h @@ -0,0 +1,498 @@ +/* + * 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. + */ + +#ifndef AVRO_VALUE_H +#define AVRO_VALUE_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <errno.h> +#include <avro/platform.h> +#include <stdlib.h> + +#include <avro/data.h> +#include <avro/schema.h> + +/* + * This file defines an interface struct for Avro data. Most of the + * interesting parts of this library will work with Avro data values + * that are expressed in whatever C type you want, as long as you can + * provide an implementation of this interface for that type. + */ + +typedef struct avro_value_iface avro_value_iface_t; + +typedef struct avro_value { + avro_value_iface_t *iface; + void *self; +} avro_value_t; + +struct avro_value_iface { + /*------------------------------------------------------------- + * "class" methods + */ + + /** + * Increment the reference count of the interface struct. This + * should be a no-op for static structs, since they don't need + * reference counts. + */ + avro_value_iface_t * + (*incref_iface)(avro_value_iface_t *iface); + + /** + * Decrement the reference count of the interface struct. If + * the count falls to 0, free the struct. This should be a + * no-op for static structs, since they don't need reference + * counts. + */ + void + (*decref_iface)(avro_value_iface_t *iface); + + /*------------------------------------------------------------- + * General "instance" methods + */ + + /** + * Increments the reference count of a value. + */ + + void + (*incref)(avro_value_t *value); + + /** + * Decrements the reference count of a value, and frees the + * value if the reference count drops to 0. After calling this + * method, your value instance is undefined, and cannot be used + * anymore. + */ + + void + (*decref)(avro_value_t *value); + + /** + * Reset the instance to its "empty", default value. You don't + * have to free the underlying storage, if you want to keep it + * around for later values. + */ + int + (*reset)(const avro_value_iface_t *iface, void *self); + + /** + * Return the general Avro type of a value instance. + */ + avro_type_t + (*get_type)(const avro_value_iface_t *iface, const void *self); + + /** + * Return the Avro schema that a value is an instance of. + */ + avro_schema_t + (*get_schema)(const avro_value_iface_t *iface, const void *self); + + /*------------------------------------------------------------- + * Primitive value getters + */ + int (*get_boolean)(const avro_value_iface_t *iface, + const void *self, int *out); + int (*get_bytes)(const avro_value_iface_t *iface, + const void *self, const void **buf, size_t *size); + int (*grab_bytes)(const avro_value_iface_t *iface, + const void *self, avro_wrapped_buffer_t *dest); + int (*get_double)(const avro_value_iface_t *iface, + const void *self, double *out); + int (*get_float)(const avro_value_iface_t *iface, + const void *self, float *out); + int (*get_int)(const avro_value_iface_t *iface, + const void *self, int32_t *out); + int (*get_long)(const avro_value_iface_t *iface, + const void *self, int64_t *out); + int (*get_null)(const avro_value_iface_t *iface, + const void *self); + /* The result will be NUL-terminated; the size will INCLUDE the + * NUL terminator. str will never be NULL unless there's an + * error. */ + int (*get_string)(const avro_value_iface_t *iface, + const void *self, const char **str, size_t *size); + int (*grab_string)(const avro_value_iface_t *iface, + const void *self, avro_wrapped_buffer_t *dest); + + int (*get_enum)(const avro_value_iface_t *iface, + const void *self, int *out); + int (*get_fixed)(const avro_value_iface_t *iface, + const void *self, const void **buf, size_t *size); + int (*grab_fixed)(const avro_value_iface_t *iface, + const void *self, avro_wrapped_buffer_t *dest); + + /*------------------------------------------------------------- + * Primitive value setters + */ + + /* + * The "give" setters can be used to give control of an existing + * buffer to a bytes, fixed, or string value. The free function + * will be called when the buffer is no longer needed. (It's + * okay for free to be NULL; that just means that nothing + * special needs to be done to free the buffer. That's useful + * for a static string, for instance.) + * + * If your class can't take control of an existing buffer, then + * your give functions should pass the buffer into the + * corresponding "set" method and then immediately free the + * buffer. + * + * Note that for strings, the free function will be called with + * a size that *includes* the NUL terminator, even though you + * provide a size that does *not*. + */ + + int (*set_boolean)(const avro_value_iface_t *iface, + void *self, int val); + int (*set_bytes)(const avro_value_iface_t *iface, + void *self, void *buf, size_t size); + int (*give_bytes)(const avro_value_iface_t *iface, + void *self, avro_wrapped_buffer_t *buf); + int (*set_double)(const avro_value_iface_t *iface, + void *self, double val); + int (*set_float)(const avro_value_iface_t *iface, + void *self, float val); + int (*set_int)(const avro_value_iface_t *iface, + void *self, int32_t val); + int (*set_long)(const avro_value_iface_t *iface, + void *self, int64_t val); + int (*set_null)(const avro_value_iface_t *iface, void *self); + /* The input must be NUL-terminated */ + int (*set_string)(const avro_value_iface_t *iface, + void *self, const char *str); + /* and size must INCLUDE the NUL terminator */ + int (*set_string_len)(const avro_value_iface_t *iface, + void *self, const char *str, size_t size); + int (*give_string_len)(const avro_value_iface_t *iface, + void *self, avro_wrapped_buffer_t *buf); + + int (*set_enum)(const avro_value_iface_t *iface, + void *self, int val); + int (*set_fixed)(const avro_value_iface_t *iface, + void *self, void *buf, size_t size); + int (*give_fixed)(const avro_value_iface_t *iface, + void *self, avro_wrapped_buffer_t *buf); + + /*------------------------------------------------------------- + * Compound value getters + */ + + /* Number of elements in array/map, or the number of fields in a + * record. */ + int (*get_size)(const avro_value_iface_t *iface, + const void *self, size_t *size); + + /* + * For arrays and maps, returns the element with the given + * index. (For maps, the "index" is based on the order that the + * keys were added to the map.) For records, returns the field + * with that index in the schema. + * + * For maps and records, the name parameter (if given) will be + * filled in with the key or field name of the returned value. + * For arrays, the name parameter will always be ignored. + */ + int (*get_by_index)(const avro_value_iface_t *iface, + const void *self, size_t index, + avro_value_t *child, const char **name); + + /* + * For maps, returns the element with the given key. For + * records, returns the element with the given field name. If + * index is given, it will be filled in with the numeric index + * of the returned value. + */ + int (*get_by_name)(const avro_value_iface_t *iface, + const void *self, const char *name, + avro_value_t *child, size_t *index); + + /* Discriminant of current union value */ + int (*get_discriminant)(const avro_value_iface_t *iface, + const void *self, int *out); + /* Current union value */ + int (*get_current_branch)(const avro_value_iface_t *iface, + const void *self, avro_value_t *branch); + + /*------------------------------------------------------------- + * Compound value setters + */ + + /* + * For all of these, the value class should know which class to + * use for its children. + */ + + /* Creates a new array element. */ + int (*append)(const avro_value_iface_t *iface, + void *self, avro_value_t *child_out, size_t *new_index); + + /* Creates a new map element, or returns an existing one. */ + int (*add)(const avro_value_iface_t *iface, + void *self, const char *key, + avro_value_t *child, size_t *index, int *is_new); + + /* Select a union branch. */ + int (*set_branch)(const avro_value_iface_t *iface, + void *self, int discriminant, + avro_value_t *branch); +}; + + +/** + * Increments the reference count of a value instance. Normally you + * don't need to call this directly; you'll have a reference whenever + * you create the value, and @ref avro_value_copy and @ref + * avro_value_move update the reference counts correctly for you. + */ + +void +avro_value_incref(avro_value_t *value); + +/** + * Decremenets the reference count of a value instance, freeing it if + * its reference count drops to 0. + */ + +void +avro_value_decref(avro_value_t *value); + +/** + * Copies a reference to a value. This does not copy any of the data + * in the value; you get two avro_value_t references that point at the + * same underlying value instance. + */ + +void +avro_value_copy_ref(avro_value_t *dest, const avro_value_t *src); + +/** + * Moves a reference to a value. This does not copy any of the data in + * the value. The @ref src value is invalidated by this function; its + * equivalent to the following: + * + * <code> + * avro_value_copy_ref(dest, src); + * avro_value_decref(src); + * </code> + */ + +void +avro_value_move_ref(avro_value_t *dest, avro_value_t *src); + +/** + * Compares two values for equality. The two values don't need to have + * the same implementation of the value interface, but they do need to + * represent Avro values of the same schema. This function ensures that + * the schemas match; if you want to skip this check, use + * avro_value_equal_fast. + */ + +int +avro_value_equal(avro_value_t *val1, avro_value_t *val2); + +/** + * Compares two values for equality. The two values don't need to have + * the same implementation of the value interface, but they do need to + * represent Avro values of the same schema. This function assumes that + * the schemas match; if you can't guarantee this, you should use + * avro_value_equal, which compares the schemas before comparing the + * values. + */ + +int +avro_value_equal_fast(avro_value_t *val1, avro_value_t *val2); + +/** + * Compares two values using the sort order defined in the Avro + * specification. The two values don't need to have the same + * implementation of the value interface, but they do need to represent + * Avro values of the same schema. This function ensures that the + * schemas match; if you want to skip this check, use + * avro_value_cmp_fast. + */ + +int +avro_value_cmp(avro_value_t *val1, avro_value_t *val2); + +/** + * Compares two values using the sort order defined in the Avro + * specification. The two values don't need to have the same + * implementation of the value interface, but they do need to represent + * Avro values of the same schema. This function assumes that the + * schemas match; if you can't guarantee this, you should use + * avro_value_cmp, which compares the schemas before comparing the + * values. + */ + +int +avro_value_cmp_fast(avro_value_t *val1, avro_value_t *val2); + + + +/** + * Copies the contents of src into dest. The two values don't need to + * have the same implementation of the value interface, but they do need + * to represent Avro values of the same schema. This function ensures + * that the schemas match; if you want to skip this check, use + * avro_value_copy_fast. + */ + +int +avro_value_copy(avro_value_t *dest, const avro_value_t *src); + +/** + * Copies the contents of src into dest. The two values don't need to + * have the same implementation of the value interface, but they do need + * to represent Avro values of the same schema. This function assumes + * that the schemas match; if you can't guarantee this, you should use + * avro_value_copy, which compares the schemas before comparing the + * values. + */ + +int +avro_value_copy_fast(avro_value_t *dest, const avro_value_t *src); + +/** + * Returns a hash value for a given Avro value. + */ + +uint32_t +avro_value_hash(avro_value_t *value); + +/* + * Returns a string containing the JSON encoding of an Avro value. You + * must free this string when you're done with it, using the standard + * free() function. (*Not* using the custom Avro allocator.) + */ + +int +avro_value_to_json(const avro_value_t *value, + int one_line, char **json_str); + + +/** + * A helper macro for calling a given method in a value instance, if + * it's present. If the value's class doesn't implement the given + * method, we return dflt. You usually won't call this directly; it's + * just here to implement the macros below. + */ + +#define avro_value_call0(value, method, dflt) \ + ((value)->iface->method == NULL? (dflt): \ + (value)->iface->method((value)->iface, (value)->self)) + +#define avro_value_call(value, method, dflt, ...) \ + ((value)->iface->method == NULL? (dflt): \ + (value)->iface->method((value)->iface, (value)->self, __VA_ARGS__)) + + +#define avro_value_iface_incref(cls) \ + ((cls)->incref_iface == NULL? (cls): (cls)->incref_iface((cls))) +#define avro_value_iface_decref(cls) \ + ((cls)->decref_iface == NULL? (void) 0: (cls)->decref_iface((cls))) + +#define avro_value_reset(value) \ + avro_value_call0(value, reset, EINVAL) +#define avro_value_get_type(value) \ + avro_value_call0(value, get_type, (avro_type_t) -1) +#define avro_value_get_schema(value) \ + avro_value_call0(value, get_schema, NULL) + +#define avro_value_get_boolean(value, out) \ + avro_value_call(value, get_boolean, EINVAL, out) +#define avro_value_get_bytes(value, buf, size) \ + avro_value_call(value, get_bytes, EINVAL, buf, size) +#define avro_value_grab_bytes(value, dest) \ + avro_value_call(value, grab_bytes, EINVAL, dest) +#define avro_value_get_double(value, out) \ + avro_value_call(value, get_double, EINVAL, out) +#define avro_value_get_float(value, out) \ + avro_value_call(value, get_float, EINVAL, out) +#define avro_value_get_int(value, out) \ + avro_value_call(value, get_int, EINVAL, out) +#define avro_value_get_long(value, out) \ + avro_value_call(value, get_long, EINVAL, out) +#define avro_value_get_null(value) \ + avro_value_call0(value, get_null, EINVAL) +#define avro_value_get_string(value, str, size) \ + avro_value_call(value, get_string, EINVAL, str, size) +#define avro_value_grab_string(value, dest) \ + avro_value_call(value, grab_string, EINVAL, dest) +#define avro_value_get_enum(value, out) \ + avro_value_call(value, get_enum, EINVAL, out) +#define avro_value_get_fixed(value, buf, size) \ + avro_value_call(value, get_fixed, EINVAL, buf, size) +#define avro_value_grab_fixed(value, dest) \ + avro_value_call(value, grab_fixed, EINVAL, dest) + +#define avro_value_set_boolean(value, val) \ + avro_value_call(value, set_boolean, EINVAL, val) +#define avro_value_set_bytes(value, buf, size) \ + avro_value_call(value, set_bytes, EINVAL, buf, size) +#define avro_value_give_bytes(value, buf) \ + avro_value_call(value, give_bytes, EINVAL, buf) +#define avro_value_set_double(value, val) \ + avro_value_call(value, set_double, EINVAL, val) +#define avro_value_set_float(value, val) \ + avro_value_call(value, set_float, EINVAL, val) +#define avro_value_set_int(value, val) \ + avro_value_call(value, set_int, EINVAL, val) +#define avro_value_set_long(value, val) \ + avro_value_call(value, set_long, EINVAL, val) +#define avro_value_set_null(value) \ + avro_value_call0(value, set_null, EINVAL) +#define avro_value_set_string(value, str) \ + avro_value_call(value, set_string, EINVAL, str) +#define avro_value_set_string_len(value, str, size) \ + avro_value_call(value, set_string_len, EINVAL, str, size) +#define avro_value_give_string_len(value, buf) \ + avro_value_call(value, give_string_len, EINVAL, buf) +#define avro_value_set_enum(value, val) \ + avro_value_call(value, set_enum, EINVAL, val) +#define avro_value_set_fixed(value, buf, size) \ + avro_value_call(value, set_fixed, EINVAL, buf, size) +#define avro_value_give_fixed(value, buf) \ + avro_value_call(value, give_fixed, EINVAL, buf) + +#define avro_value_get_size(value, size) \ + avro_value_call(value, get_size, EINVAL, size) +#define avro_value_get_by_index(value, idx, child, name) \ + avro_value_call(value, get_by_index, EINVAL, idx, child, name) +#define avro_value_get_by_name(value, name, child, index) \ + avro_value_call(value, get_by_name, EINVAL, name, child, index) +#define avro_value_get_discriminant(value, out) \ + avro_value_call(value, get_discriminant, EINVAL, out) +#define avro_value_get_current_branch(value, branch) \ + avro_value_call(value, get_current_branch, EINVAL, branch) + +#define avro_value_append(value, child, new_index) \ + avro_value_call(value, append, EINVAL, child, new_index) +#define avro_value_add(value, key, child, index, is_new) \ + avro_value_call(value, add, EINVAL, key, child, index, is_new) +#define avro_value_set_branch(value, discriminant, branch) \ + avro_value_call(value, set_branch, EINVAL, discriminant, branch) + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro_generic_internal.h b/fluent-bit/lib/avro/src/avro_generic_internal.h new file mode 100644 index 000000000..9843ed652 --- /dev/null +++ b/fluent-bit/lib/avro/src/avro_generic_internal.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef AVRO_GENERIC_INTERNAL_H +#define AVRO_GENERIC_INTERNAL_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <sys/types.h> + +#include "avro/generic.h" +#include "avro/schema.h" +#include "avro/value.h" + +/* + * Each generic value implementation struct defines a couple of extra + * methods that we use to control the lifecycle of the value objects. + */ + +typedef struct avro_generic_value_iface { + avro_value_iface_t parent; + + /** + * Return the size of an instance of this value type. If this + * returns 0, then this value type can't be used with any + * function or type (like avro_value_new) that expects to + * allocate space for the value itself. + */ + size_t + (*instance_size)(const avro_value_iface_t *iface); + + /** + * Initialize a new value instance. + */ + int + (*init)(const avro_value_iface_t *iface, void *self); + + /** + * Finalize a value instance. + */ + void + (*done)(const avro_value_iface_t *iface, void *self); +} avro_generic_value_iface_t; + + +#define avro_value_instance_size(gcls) \ + ((gcls)->instance_size == NULL ? (ssize_t)-1 : (ssize_t)(gcls)->instance_size(&(gcls)->parent)) +#define avro_value_init(gcls, self) \ + ((gcls)->init == NULL? EINVAL: (gcls)->init(&(gcls)->parent, (self))) +#define avro_value_done(gcls, self) \ + ((gcls)->done == NULL? (void) 0: (gcls)->done(&(gcls)->parent, (self))) + + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avro_private.h b/fluent-bit/lib/avro/src/avro_private.h new file mode 100644 index 000000000..f97ef6b5a --- /dev/null +++ b/fluent-bit/lib/avro/src/avro_private.h @@ -0,0 +1,99 @@ +/* + * 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. + */ +#ifndef AVRO_PRIVATE_H +#define AVRO_PRIVATE_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <errno.h> + +#include "avro/errors.h" +#include "avro/platform.h" + +#ifdef HAVE_CONFIG_H +/* This is only true for now in the autotools build */ +#include "config.h" +#endif + +#ifdef _WIN32 +#define snprintf _snprintf +#endif + +/* Note that AVRO_PLATFORM_IS_BIG_ENDIAN is *always* defined. It is + * either TRUE (1) or FALSE (0). + */ +#ifdef _WIN32 + #define AVRO_PLATFORM_IS_BIG_ENDIAN (0) +#else // UNIX + #include <sys/param.h> + #if BYTE_ORDER == BIG_ENDIAN + #define AVRO_PLATFORM_IS_BIG_ENDIAN (1) + #else + #define AVRO_PLATFORM_IS_BIG_ENDIAN (0) + #endif +#endif + +/* Add definition of EILSEQ if it is not defined in errno.h. */ +#include <errno.h> +#ifndef EILSEQ +#define EILSEQ 138 +#endif + + +#define check(rval, call) { rval = call; if(rval) return rval; } + +#define check_set(rval, call, ...) \ + { \ + rval = call; \ + if (rval) { \ + avro_set_error(__VA_ARGS__); \ + return rval; \ + } \ + } + +#define check_prefix(rval, call, ...) \ + { \ + rval = call; \ + if (rval) { \ + avro_prefix_error(__VA_ARGS__); \ + return rval; \ + } \ + } + +#define check_param(result, test, name) \ + { \ + if (!(test)) { \ + avro_set_error("Invalid " name " in %s", \ + __FUNCTION__); \ + return result; \ + } \ + } + +#define AVRO_UNUSED(var) (void)var; + +#define container_of(ptr_, type_, member_) \ + ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_)) + +#define nullstrcmp(s1, s2) \ + (((s1) && (s2)) ? strcmp(s1, s2) : ((s1) || (s2))) + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/avroappend.c b/fluent-bit/lib/avro/src/avroappend.c new file mode 100644 index 000000000..7243c6009 --- /dev/null +++ b/fluent-bit/lib/avro/src/avroappend.c @@ -0,0 +1,169 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef _WIN32 +#include <unistd.h> +#endif + +#include "avro.h" + +int process_file(const char *in_filename, const char *out_filename) +{ + avro_file_reader_t reader; + avro_file_writer_t writer; + + if (in_filename == NULL) { + if (avro_file_reader_fp(stdin, "<stdin>", 0, &reader)) { + fprintf(stderr, "Error opening <stdin>:\n %s\n", + avro_strerror()); + return 1; + } + } else { + if (avro_file_reader(in_filename, &reader)) { + fprintf(stderr, "Error opening %s:\n %s\n", + in_filename, avro_strerror()); + return 1; + } + } + + avro_schema_t wschema; + wschema = avro_file_reader_get_writer_schema(reader); + + /* Check that the reader schema is the same as the writer schema */ + { + avro_schema_t oschema; + avro_file_reader_t oreader; + + if (avro_file_reader(out_filename, &oreader)) { + fprintf(stderr, "Error opening %s:\n %s\n", + out_filename, avro_strerror()); + avro_file_reader_close(reader); + return 1; + } + + oschema = avro_file_reader_get_writer_schema(oreader); + + if (avro_schema_equal(oschema, wschema) == 0) { + fprintf(stderr, "Error: reader and writer schema are not equal.\n"); + avro_file_reader_close(oreader); + avro_file_reader_close(reader); + return 1; + } + + avro_file_reader_close(oreader); + avro_schema_decref(oschema); + } + + if (avro_file_writer_open(out_filename, &writer)) { + fprintf(stderr, "Error opening %s:\n %s\n", + out_filename, avro_strerror()); + avro_file_reader_close(reader); + return 1; + } + + avro_value_iface_t *iface; + avro_value_t value; + + iface = avro_generic_class_from_schema(wschema); + avro_generic_value_new(iface, &value); + + while (avro_file_reader_read_value(reader, &value) == 0) { + if (avro_file_writer_append_value(writer, &value)) { + fprintf(stderr, "Error writing to %s:\n %s\n", + out_filename, avro_strerror()); + return 1; + } + avro_value_reset(&value); + } + + avro_file_reader_close(reader); + avro_file_writer_close(writer); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(wschema); + + return 0; +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: avroappend [<input avro file>] <output avro file>\n"); +} + +static int check_filenames(const char *in_filename, const char *out_filename) +{ + if (in_filename == NULL) { + return 0; + } + + struct stat in_stat; + struct stat out_stat; + + if (stat(in_filename, &in_stat) == -1) { + fprintf(stderr, "stat error on %s: %s\n", in_filename, strerror(errno)); + return 2; + } + + if (stat(out_filename, &out_stat) == -1) { + fprintf(stderr, "stat error on %s: %s\n", out_filename, strerror(errno)); + return 2; + } + + if (in_stat.st_dev == out_stat.st_dev && in_stat.st_ino == out_stat.st_ino) { + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + char *in_filename; + char *out_filename; + + argc--; + argv++; + + if (argc == 2) { + in_filename = argv[0]; + out_filename = argv[1]; + } else if (argc == 1) { + in_filename = NULL; + out_filename = argv[0]; + } else { + fprintf(stderr, "Not enough arguments\n\n"); + usage(); + exit(1); + } + + int ret = check_filenames(in_filename, out_filename); + + if (ret == 1) { + fprintf(stderr, "Files are the same.\n"); + } + + if (ret > 0) { + exit(1); + } + + exit(process_file(in_filename, out_filename)); +} diff --git a/fluent-bit/lib/avro/src/avrocat.c b/fluent-bit/lib/avro/src/avrocat.c new file mode 100644 index 000000000..134ea8e64 --- /dev/null +++ b/fluent-bit/lib/avro/src/avrocat.c @@ -0,0 +1,127 @@ +/* + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "avro.h" +#include "avro_private.h" + + +/*-- PROCESSING A FILE --*/ + +static void +process_file(const char *filename) +{ + avro_file_reader_t reader; + FILE *fp; + int should_close; + + if (filename == NULL) { + fp = stdin; + filename = "<stdin>"; + should_close = 0; + } else { + fp = fopen(filename, "rb"); + should_close = 1; + + if (fp == NULL) { + fprintf(stderr, "Error opening %s:\n %s\n", + filename, strerror(errno)); + exit(1); + } + } + + if (avro_file_reader_fp(fp, filename, 0, &reader)) { + fprintf(stderr, "Error opening %s:\n %s\n", + filename, avro_strerror()); + if (should_close) { + fclose(fp); + } + exit(1); + } + + avro_schema_t wschema; + avro_value_iface_t *iface; + avro_value_t value; + + wschema = avro_file_reader_get_writer_schema(reader); + iface = avro_generic_class_from_schema(wschema); + avro_generic_value_new(iface, &value); + + int rval; + + while ((rval = avro_file_reader_read_value(reader, &value)) == 0) { + char *json; + + if (avro_value_to_json(&value, 1, &json)) { + fprintf(stderr, "Error converting value to JSON: %s\n", + avro_strerror()); + } else { + printf("%s\n", json); + free(json); + } + + avro_value_reset(&value); + } + + // If it was not an EOF that caused it to fail, + // print the error. + if (rval != EOF) { + fprintf(stderr, "Error: %s\n", avro_strerror()); + } + + avro_file_reader_close(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(wschema); + + if (should_close) { + fclose(fp); + } +} + + +/*-- MAIN PROGRAM --*/ + +static void usage(void) +{ + fprintf(stderr, + "Usage: avrocat <avro data file>\n"); +} + + +int main(int argc, char **argv) +{ + char *data_filename; + + if (argc == 2) { + data_filename = argv[1]; + } else if (argc == 1) { + data_filename = NULL; + } else { + fprintf(stderr, "Can't read from multiple input files.\n"); + usage(); + exit(1); + } + + /* Process the data file */ + process_file(data_filename); + return 0; +} diff --git a/fluent-bit/lib/avro/src/avromod.c b/fluent-bit/lib/avro/src/avromod.c new file mode 100644 index 000000000..7415d6e2f --- /dev/null +++ b/fluent-bit/lib/avro/src/avromod.c @@ -0,0 +1,168 @@ +/* + * 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 <errno.h> +#include <getopt.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "avro.h" +#include "avro_private.h" + + +/* The compression codec to use. */ +static const char *codec = "null"; + +/* The block size to use. */ +static size_t block_size = 0; + +/*-- PROCESSING A FILE --*/ + +static void +process_file(const char *in_filename, const char *out_filename) +{ + avro_file_reader_t reader; + avro_file_writer_t writer; + + if (in_filename == NULL) { + if (avro_file_reader_fp(stdin, "<stdin>", 0, &reader)) { + fprintf(stderr, "Error opening <stdin>:\n %s\n", + avro_strerror()); + exit(1); + } + } else { + if (avro_file_reader(in_filename, &reader)) { + fprintf(stderr, "Error opening %s:\n %s\n", + in_filename, avro_strerror()); + exit(1); + } + } + + avro_schema_t wschema; + avro_value_iface_t *iface; + avro_value_t value; + int rval; + + wschema = avro_file_reader_get_writer_schema(reader); + iface = avro_generic_class_from_schema(wschema); + avro_generic_value_new(iface, &value); + + if (avro_file_writer_create_with_codec + (out_filename, wschema, &writer, codec, block_size)) { + fprintf(stderr, "Error creating %s:\n %s\n", + out_filename, avro_strerror()); + exit(1); + } + + while ((rval = avro_file_reader_read_value(reader, &value)) == 0) { + if (avro_file_writer_append_value(writer, &value)) { + fprintf(stderr, "Error writing to %s:\n %s\n", + out_filename, avro_strerror()); + exit(1); + } + avro_value_reset(&value); + } + + if (rval != EOF) { + fprintf(stderr, "Error reading value: %s", avro_strerror()); + } + + avro_file_reader_close(reader); + avro_file_writer_close(writer); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(wschema); +} + + +/*-- MAIN PROGRAM --*/ + +static struct option longopts[] = { + { "block-size", required_argument, NULL, 'b' }, + { "codec", required_argument, NULL, 'c' }, + { NULL, 0, NULL, 0 } +}; + +static void usage(void) +{ + fprintf(stderr, + "Usage: avromod [--codec=<compression codec>]\n" + " [--block-size=<block size>]\n" + " [<input avro file>]\n" + " <output avro file>\n"); +} + +static void +parse_block_size(const char *optarg) +{ + unsigned long ul; + char *end; + + ul = strtoul(optarg, &end, 10); + if ((ul == 0 && end == optarg) || + (ul == ULONG_MAX && errno == ERANGE)) { + fprintf(stderr, "Invalid block size: %s\n\n", optarg); + usage(); + exit(1); + } + block_size = ul; +} + + +int main(int argc, char **argv) +{ + char *in_filename; + char *out_filename; + + int ch; + while ((ch = getopt_long(argc, argv, "b:c:", longopts, NULL)) != -1) { + switch (ch) { + case 'b': + parse_block_size(optarg); + break; + + case 'c': + codec = optarg; + break; + + default: + usage(); + exit(1); + } + } + + argc -= optind; + argv += optind; + + if (argc == 2) { + in_filename = argv[0]; + out_filename = argv[1]; + } else if (argc == 1) { + in_filename = NULL; + out_filename = argv[0]; + } else { + fprintf(stderr, "Can't read from multiple input files.\n"); + usage(); + exit(1); + } + + /* Process the data file */ + process_file(in_filename, out_filename); + return 0; +} diff --git a/fluent-bit/lib/avro/src/avropipe.c b/fluent-bit/lib/avro/src/avropipe.c new file mode 100644 index 000000000..7bda12534 --- /dev/null +++ b/fluent-bit/lib/avro/src/avropipe.c @@ -0,0 +1,432 @@ +/* + * 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 <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <avro/platform.h> +#include <avro/platform.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "avro.h" +#include "avro_private.h" + + +/* The path separator to use in the JSON output. */ + +static const char *separator = "/"; + + +/*-- PROCESSING A FILE --*/ + +/** + * Fills in a raw string with the path to an element of an array. + */ + +static void +create_array_prefix(avro_raw_string_t *dest, const char *prefix, size_t index) +{ + static char buf[100]; + snprintf(buf, sizeof(buf), "%" PRIsz, index); + avro_raw_string_set(dest, prefix); + avro_raw_string_append(dest, separator); + avro_raw_string_append(dest, buf); +} + +static void +create_object_prefix(avro_raw_string_t *dest, const char *prefix, const char *key) +{ + /* + * Make sure that the key doesn't contain the separator + * character. + */ + + if (strstr(key, separator) != NULL) { + fprintf(stderr, + "Error: Element \"%s\" in object %s " + "contains the separator character.\n" + "Please use the --separator option to choose another.\n", + key, prefix); + exit(1); + } + + avro_raw_string_set(dest, prefix); + avro_raw_string_append(dest, separator); + avro_raw_string_append(dest, key); +} + +static void +print_bytes_value(const char *buf, size_t size) +{ + size_t i; + printf("\""); + for (i = 0; i < size; i++) + { + if (buf[i] == '"') { + printf("\\\""); + } else if (buf[i] == '\\') { + printf("\\\\"); + } else if (buf[i] == '\b') { + printf("\\b"); + } else if (buf[i] == '\f') { + printf("\\f"); + } else if (buf[i] == '\n') { + printf("\\n"); + } else if (buf[i] == '\r') { + printf("\\r"); + } else if (buf[i] == '\t') { + printf("\\t"); + } else if (isprint(buf[i])) { + printf("%c", (int) buf[i]); + } else { + printf("\\u00%02x", (unsigned int) (unsigned char) buf[i]); + } + } + printf("\""); +} + +static void +process_value(const char *prefix, avro_value_t *value); + +static void +process_array(const char *prefix, avro_value_t *value) +{ + printf("%s\t[]\n", prefix); + size_t element_count; + avro_value_get_size(value, &element_count); + + avro_raw_string_t element_prefix; + avro_raw_string_init(&element_prefix); + + size_t i; + for (i = 0; i < element_count; i++) { + avro_value_t element_value; + avro_value_get_by_index(value, i, &element_value, NULL); + + create_array_prefix(&element_prefix, prefix, i); + process_value((const char *) avro_raw_string_get(&element_prefix), &element_value); + } + + avro_raw_string_done(&element_prefix); +} + +static void +process_enum(const char *prefix, avro_value_t *value) +{ + int val; + const char *symbol_name; + + avro_schema_t schema = avro_value_get_schema(value); + avro_value_get_enum(value, &val); + symbol_name = avro_schema_enum_get(schema, val); + printf("%s\t", prefix); + print_bytes_value(symbol_name, strlen(symbol_name)); + printf("\n"); +} + +static void +process_map(const char *prefix, avro_value_t *value) +{ + printf("%s\t{}\n", prefix); + size_t element_count; + avro_value_get_size(value, &element_count); + + avro_raw_string_t element_prefix; + avro_raw_string_init(&element_prefix); + + size_t i; + for (i = 0; i < element_count; i++) { + const char *key; + avro_value_t element_value; + avro_value_get_by_index(value, i, &element_value, &key); + + create_object_prefix(&element_prefix, prefix, key); + process_value((const char *) avro_raw_string_get(&element_prefix), &element_value); + } + + avro_raw_string_done(&element_prefix); +} + +static void +process_record(const char *prefix, avro_value_t *value) +{ + printf("%s\t{}\n", prefix); + size_t field_count; + avro_value_get_size(value, &field_count); + + avro_raw_string_t field_prefix; + avro_raw_string_init(&field_prefix); + + size_t i; + for (i = 0; i < field_count; i++) { + avro_value_t field_value; + const char *field_name; + avro_value_get_by_index(value, i, &field_value, &field_name); + + create_object_prefix(&field_prefix, prefix, field_name); + process_value((const char *) avro_raw_string_get(&field_prefix), &field_value); + } + + avro_raw_string_done(&field_prefix); +} + +static void +process_union(const char *prefix, avro_value_t *value) +{ + avro_value_t branch_value; + avro_value_get_current_branch(value, &branch_value); + + /* nulls in a union aren't wrapped in a JSON object */ + if (avro_value_get_type(&branch_value) == AVRO_NULL) { + printf("%s\tnull\n", prefix); + return; + } + + int discriminant; + avro_value_get_discriminant(value, &discriminant); + + avro_schema_t schema = avro_value_get_schema(value); + avro_schema_t branch_schema = avro_schema_union_branch(schema, discriminant); + const char *branch_name = avro_schema_type_name(branch_schema); + + avro_raw_string_t branch_prefix; + avro_raw_string_init(&branch_prefix); + create_object_prefix(&branch_prefix, prefix, branch_name); + + printf("%s\t{}\n", prefix); + process_value((const char *) avro_raw_string_get(&branch_prefix), &branch_value); + + avro_raw_string_done(&branch_prefix); +} + +static void +process_value(const char *prefix, avro_value_t *value) +{ + avro_type_t type = avro_value_get_type(value); + switch (type) { + case AVRO_BOOLEAN: + { + int val; + avro_value_get_boolean(value, &val); + printf("%s\t%s\n", prefix, val? "true": "false"); + return; + } + + case AVRO_BYTES: + { + const void *buf; + size_t size; + avro_value_get_bytes(value, &buf, &size); + printf("%s\t", prefix); + print_bytes_value((const char *) buf, size); + printf("\n"); + return; + } + + case AVRO_DOUBLE: + { + double val; + avro_value_get_double(value, &val); + printf("%s\t%lf\n", prefix, val); + return; + } + + case AVRO_FLOAT: + { + float val; + avro_value_get_float(value, &val); + printf("%s\t%f\n", prefix, val); + return; + } + + case AVRO_INT32: + { + int32_t val; + avro_value_get_int(value, &val); + printf("%s\t%" PRId32 "\n", prefix, val); + return; + } + + case AVRO_INT64: + { + int64_t val; + avro_value_get_long(value, &val); + printf("%s\t%" PRId64 "\n", prefix, val); + return; + } + + case AVRO_NULL: + { + avro_value_get_null(value); + printf("%s\tnull\n", prefix); + return; + } + + case AVRO_STRING: + { + /* TODO: Convert the UTF-8 to the current + * locale's character set */ + const char *buf; + size_t size; + avro_value_get_string(value, &buf, &size); + printf("%s\t", prefix); + /* For strings, size includes the NUL terminator. */ + print_bytes_value(buf, size-1); + printf("\n"); + return; + } + + case AVRO_ARRAY: + process_array(prefix, value); + return; + + case AVRO_ENUM: + process_enum(prefix, value); + return; + + case AVRO_FIXED: + { + const void *buf; + size_t size; + avro_value_get_fixed(value, &buf, &size); + printf("%s\t", prefix); + print_bytes_value((const char *) buf, size); + printf("\n"); + return; + } + + case AVRO_MAP: + process_map(prefix, value); + return; + + case AVRO_RECORD: + process_record(prefix, value); + return; + + case AVRO_UNION: + process_union(prefix, value); + return; + + default: + { + fprintf(stderr, "Unknown schema type\n"); + exit(1); + } + } +} + +static void +process_file(const char *filename) +{ + avro_file_reader_t reader; + + if (filename == NULL) { + if (avro_file_reader_fp(stdin, "<stdin>", 0, &reader)) { + fprintf(stderr, "Error opening <stdin>:\n %s\n", + avro_strerror()); + exit(1); + } + } else { + if (avro_file_reader(filename, &reader)) { + fprintf(stderr, "Error opening %s:\n %s\n", + filename, avro_strerror()); + exit(1); + } + } + + /* The JSON root is an array */ + printf("%s\t[]\n", separator); + + avro_raw_string_t prefix; + avro_raw_string_init(&prefix); + + avro_schema_t wschema = avro_file_reader_get_writer_schema(reader); + avro_value_iface_t *iface = avro_generic_class_from_schema(wschema); + avro_value_t value; + avro_generic_value_new(iface, &value); + + size_t record_number = 0; + int rval; + + for (; (rval = avro_file_reader_read_value(reader, &value)) == 0; record_number++) { + create_array_prefix(&prefix, "", record_number); + process_value((const char *) avro_raw_string_get(&prefix), &value); + avro_value_reset(&value); + } + + if (rval != EOF) { + fprintf(stderr, "Error reading value: %s", avro_strerror()); + } + + avro_raw_string_done(&prefix); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_file_reader_close(reader); + avro_schema_decref(wschema); +} + + +/*-- MAIN PROGRAM --*/ +static struct option longopts[] = { + { "separator", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } +}; + +static void usage(void) +{ + fprintf(stderr, + "Usage: avropipe [--separator=<separator>]\n" + " <avro data file>\n"); +} + + +int main(int argc, char **argv) +{ + char *data_filename; + + int ch; + while ((ch = getopt_long(argc, argv, "s:", longopts, NULL) ) != -1) { + switch (ch) { + case 's': + separator = optarg; + break; + + default: + usage(); + exit(1); + } + } + + argc -= optind; + argv += optind; + + if (argc == 1) { + data_filename = argv[0]; + } else if (argc == 0) { + data_filename = NULL; + } else { + fprintf(stderr, "Can't read from multiple input files.\n"); + usage(); + exit(1); + } + + /* Process the data file */ + process_file(data_filename); + return 0; +} diff --git a/fluent-bit/lib/avro/src/codec.c b/fluent-bit/lib/avro/src/codec.c new file mode 100644 index 000000000..613a91437 --- /dev/null +++ b/fluent-bit/lib/avro/src/codec.c @@ -0,0 +1,620 @@ +/* + * 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 <string.h> +#ifdef SNAPPY_CODEC +#include <snappy-c.h> +# if defined(__APPLE__) +# include <libkern/OSByteOrder.h> +# define __bswap_32 OSSwapInt32 +# elif defined(__FreeBSD__) +# include <sys/endian.h> +# define __bswap_32 bswap32 +# elif defined(_WIN32) +# include <stdlib.h> +# define __bswap_32 _byteswap_ulong +# else +# include <byteswap.h> +# endif +#endif +#ifdef DEFLATE_CODEC +#include <zlib.h> +#endif +#ifdef LZMA_CODEC +#include <lzma.h> +#endif +#include "avro/errors.h" +#include "avro/allocation.h" +#include "codec.h" + +#define DEFAULT_BLOCK_SIZE (16 * 1024) + +/* NULL codec */ + +static int +codec_null(avro_codec_t codec) +{ + codec->name = "null"; + codec->type = AVRO_CODEC_NULL; + codec->block_size = 0; + codec->used_size = 0; + codec->block_data = NULL; + codec->codec_data = NULL; + + return 0; +} + +static int encode_null(avro_codec_t c, void * data, int64_t len) +{ + c->block_data = data; + c->block_size = len; + c->used_size = len; + + return 0; +} + +static int decode_null(avro_codec_t c, void * data, int64_t len) +{ + c->block_data = data; + c->block_size = len; + c->used_size = len; + + return 0; +} + +static int reset_null(avro_codec_t c) +{ + c->block_data = NULL; + c->block_size = 0; + c->used_size = 0; + c->codec_data = NULL; + + return 0; +} + +/* Snappy codec */ + +#ifdef SNAPPY_CODEC + +static int +codec_snappy(avro_codec_t codec) +{ + codec->name = "snappy"; + codec->type = AVRO_CODEC_SNAPPY; + codec->block_size = 0; + codec->used_size = 0; + codec->block_data = NULL; + codec->codec_data = NULL; + + return 0; +} + +static int encode_snappy(avro_codec_t c, void * data, int64_t len) +{ + uint32_t crc; + size_t outlen = snappy_max_compressed_length(len); + + if (!c->block_data) { + c->block_data = avro_malloc(outlen+4); + c->block_size = outlen+4; + } else if (c->block_size < (int64_t) (outlen+4)) { + c->block_data = avro_realloc(c->block_data, c->block_size, (outlen+4)); + c->block_size = outlen+4; + } + + if (!c->block_data) { + avro_set_error("Cannot allocate memory for snappy"); + return 1; + } + + if (snappy_compress((const char *)data, len, (char*)c->block_data, &outlen) != SNAPPY_OK) + { + avro_set_error("Error compressing block with Snappy"); + return 1; + } + + crc = __bswap_32(crc32(0, (const Bytef *)data, len)); + memcpy((char*)c->block_data+outlen, &crc, 4); + c->used_size = outlen+4; + + return 0; +} + +static int decode_snappy(avro_codec_t c, void * data, int64_t len) +{ + uint32_t crc; + size_t outlen; + + if (snappy_uncompressed_length((const char*)data, len-4, &outlen) != SNAPPY_OK) { + avro_set_error("Uncompressed length error in snappy"); + return 1; + } + + if (!c->block_data) { + c->block_data = avro_malloc(outlen); + c->block_size = outlen; + } else if ( (size_t)c->block_size < outlen) { + c->block_data = avro_realloc(c->block_data, c->block_size, outlen); + c->block_size = outlen; + } + + if (!c->block_data) + { + avro_set_error("Cannot allocate memory for snappy"); + return 1; + } + + if (snappy_uncompress((const char*)data, len-4, (char*)c->block_data, &outlen) != SNAPPY_OK) + { + avro_set_error("Error uncompressing block with Snappy"); + return 1; + } + + crc = __bswap_32(crc32(0, (const Bytef *)c->block_data, outlen)); + if (memcmp(&crc, (char*)data+len-4, 4)) + { + avro_set_error("CRC32 check failure uncompressing block with Snappy"); + return 1; + } + + c->used_size = outlen; + + return 0; +} + +static int reset_snappy(avro_codec_t c) +{ + if (c->block_data) { + avro_free(c->block_data, c->block_size); + } + + c->block_data = NULL; + c->block_size = 0; + c->used_size = 0; + c->codec_data = NULL; + + return 0; +} + +#endif // SNAPPY_CODEC + +/* Deflate codec */ + +#ifdef DEFLATE_CODEC + +struct codec_data_deflate { + z_stream deflate; + z_stream inflate; +}; +#define codec_data_deflate_stream(cd) &((struct codec_data_deflate *)cd)->deflate +#define codec_data_inflate_stream(cd) &((struct codec_data_deflate *)cd)->inflate + + +static int +codec_deflate(avro_codec_t codec) +{ + codec->name = "deflate"; + codec->type = AVRO_CODEC_DEFLATE; + codec->block_size = 0; + codec->used_size = 0; + codec->block_data = NULL; + codec->codec_data = avro_new(struct codec_data_deflate); + + if (!codec->codec_data) { + avro_set_error("Cannot allocate memory for zlib"); + return 1; + } + + z_stream *ds = codec_data_deflate_stream(codec->codec_data); + z_stream *is = codec_data_inflate_stream(codec->codec_data); + + memset(ds, 0, sizeof(z_stream)); + memset(is, 0, sizeof(z_stream)); + + ds->zalloc = is->zalloc = Z_NULL; + ds->zfree = is->zfree = Z_NULL; + ds->opaque = is->opaque = Z_NULL; + + if (deflateInit2(ds, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + avro_freet(struct codec_data_deflate, codec->codec_data); + codec->codec_data = NULL; + avro_set_error("Cannot initialize zlib deflate"); + return 1; + } + + if (inflateInit2(is, -15) != Z_OK) { + avro_freet(struct codec_data_deflate, codec->codec_data); + codec->codec_data = NULL; + avro_set_error("Cannot initialize zlib inflate"); + return 1; + } + + return 0; +} + +static int encode_deflate(avro_codec_t c, void * data, int64_t len) +{ + int err; + int64_t defl_len = compressBound((uLong)len * 1.2); + + if (!c->block_data) { + c->block_data = avro_malloc(defl_len); + c->block_size = defl_len; + } else if ( c->block_size < defl_len) { + c->block_data = avro_realloc(c->block_data, c->block_size, defl_len); + c->block_size = defl_len; + } + + if (!c->block_data) + { + avro_set_error("Cannot allocate memory for deflate"); + return 1; + } + + c->used_size = 0; + + z_stream *s = codec_data_deflate_stream(c->codec_data); + + s->next_in = (Bytef*)data; + s->avail_in = (uInt)len; + + s->next_out = c->block_data; + s->avail_out = (uInt)c->block_size; + + s->total_out = 0; + + err = deflate(s, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(s); + if (err != Z_OK) { + avro_set_error("Error compressing block with deflate (%i)", err); + return 1; + } + return 0; + } + + // zlib resizes the buffer? + c->block_size = s->total_out; + c->used_size = s->total_out; + + if (deflateReset(s) != Z_OK) { + return 1; + } + + return 0; +} + +static int decode_deflate(avro_codec_t c, void * data, int64_t len) +{ + int err; + z_stream *s = codec_data_inflate_stream(c->codec_data); + + if (!c->block_data) { + c->block_data = avro_malloc(DEFAULT_BLOCK_SIZE); + c->block_size = DEFAULT_BLOCK_SIZE; + } + + if (!c->block_data) + { + avro_set_error("Cannot allocate memory for deflate"); + return 1; + } + + c->used_size = 0; + + s->next_in = data; + s->avail_in = len; + + s->next_out = c->block_data; + s->avail_out = c->block_size; + + s->total_out = 0; + + do + { + err = inflate(s, Z_FINISH); + + // Apparently if there is yet available space in the output then something + // has gone wrong in decompressing the data (according to cpython zlibmodule.c) + if (err == Z_BUF_ERROR && s->avail_out > 0) { + inflateEnd(s); + avro_set_error("Error decompressing block with deflate, possible data error"); + return 1; + } + + // The buffer was not big enough. resize it. + if (err == Z_BUF_ERROR) + { + c->block_data = avro_realloc(c->block_data, c->block_size, c->block_size * 2); + s->next_out = c->block_data + s->total_out; + s->avail_out += c->block_size; + c->block_size = c->block_size * 2; + } + } while (err == Z_BUF_ERROR); + + if (err != Z_STREAM_END) { + inflateEnd(s); + if (err != Z_OK) { + avro_set_error("Error decompressing block with deflate (%i)", err); + return 1; + } + return 0; + } + + c->used_size = s->total_out; + + if (inflateReset(s) != Z_OK) { + avro_set_error("Error resetting deflate decompression"); + return 1; + } + + return 0; +} + +static int reset_deflate(avro_codec_t c) +{ + if (c->block_data) { + avro_free(c->block_data, c->block_size); + } + if (c->codec_data) { + deflateEnd(codec_data_deflate_stream(c->codec_data)); + inflateEnd(codec_data_inflate_stream(c->codec_data)); + avro_freet(struct codec_data_deflate, c->codec_data); + } + + c->block_data = NULL; + c->block_size = 0; + c->used_size = 0; + c->codec_data = NULL; + + return 0; +} + +#endif // DEFLATE_CODEC + +/* LZMA codec */ + +#ifdef LZMA_CODEC + +struct codec_data_lzma { + lzma_filter filters[2]; + lzma_options_lzma options; +}; +#define codec_data_lzma_filters(cd) ((struct codec_data_lzma *)cd)->filters +#define codec_data_lzma_options(cd) &((struct codec_data_lzma *)cd)->options + +static int +codec_lzma(avro_codec_t codec) +{ + codec->name = "lzma"; + codec->type = AVRO_CODEC_LZMA; + codec->block_size = 0; + codec->used_size = 0; + codec->block_data = NULL; + codec->codec_data = avro_new(struct codec_data_lzma); + + if (!codec->codec_data) { + avro_set_error("Cannot allocate memory for lzma"); + return 1; + } + + lzma_options_lzma* opt = codec_data_lzma_options(codec->codec_data); + lzma_lzma_preset(opt, LZMA_PRESET_DEFAULT); + + lzma_filter* filters = codec_data_lzma_filters(codec->codec_data); + filters[0].id = LZMA_FILTER_LZMA2; + filters[0].options = opt; + filters[1].id = LZMA_VLI_UNKNOWN; + filters[1].options = NULL; + + return 0; +} + +static int encode_lzma(avro_codec_t codec, void * data, int64_t len) +{ + lzma_ret ret; + size_t written = 0; + lzma_filter* filters = codec_data_lzma_filters(codec->codec_data); + + int64_t buff_len = len + lzma_raw_encoder_memusage(filters); + + if (!codec->block_data) { + codec->block_data = avro_malloc(buff_len); + codec->block_size = buff_len; + } + + if (!codec->block_data) + { + avro_set_error("Cannot allocate memory for lzma encoder"); + return 1; + } + + ret = lzma_raw_buffer_encode(filters, NULL, data, len, codec->block_data, &written, codec->block_size); + + codec->used_size = written; + + if (ret != LZMA_OK) { + avro_set_error("Error in lzma encoder"); + return 1; + } + + return 0; +} + +static int decode_lzma(avro_codec_t codec, void * data, int64_t len) +{ + size_t read_pos = 0; + size_t write_pos = 0; + lzma_ret ret; + lzma_filter* filters = codec_data_lzma_filters(codec->codec_data); + + if (!codec->block_data) { + codec->block_data = avro_malloc(DEFAULT_BLOCK_SIZE); + codec->block_size = DEFAULT_BLOCK_SIZE; + } + + if (!codec->block_data) { + avro_set_error("Cannot allocate memory for lzma decoder"); + return 1; + } + + do + { + ret = lzma_raw_buffer_decode(filters, NULL, data, + &read_pos, len, codec->block_data, &write_pos, + codec->block_size); + + codec->used_size = write_pos; + + // If it ran out of space to decode, give it more!! + // It will continue where it left off because of read_pos and write_pos. + if (ret == LZMA_BUF_ERROR) { + codec->block_data = avro_realloc(codec->block_data, codec->block_size, codec->block_size * 2); + codec->block_size = codec->block_size * 2; + } + + } while (ret == LZMA_BUF_ERROR); + + if (ret != LZMA_OK) { + avro_set_error("Error in lzma decoder"); + return 1; + } + + return 0; +} + +static int reset_lzma(avro_codec_t c) +{ + if (c->block_data) { + avro_free(c->block_data, c->block_size); + } + if (c->codec_data) { + avro_freet(struct codec_data_lzma, c->codec_data); + } + + c->block_data = NULL; + c->block_size = 0; + c->used_size = 0; + c->codec_data = NULL; + + return 0; +} + +#endif // LZMA_CODEC + +/* Common interface */ + +int avro_codec(avro_codec_t codec, const char *type) +{ + if (type == NULL) { + return codec_null(codec); + } + +#ifdef SNAPPY_CODEC + if (strcmp("snappy", type) == 0) { + return codec_snappy(codec); + } +#endif + +#ifdef DEFLATE_CODEC + if (strcmp("deflate", type) == 0) { + return codec_deflate(codec); + } +#endif + +#ifdef LZMA_CODEC + if (strcmp("lzma", type) == 0) { + return codec_lzma(codec); + } +#endif + + if (strcmp("null", type) == 0) { + return codec_null(codec); + } + + avro_set_error("Unknown codec %s", type); + return 1; +} + +int avro_codec_encode(avro_codec_t c, void * data, int64_t len) +{ + switch(c->type) + { + case AVRO_CODEC_NULL: + return encode_null(c, data, len); +#ifdef SNAPPY_CODEC + case AVRO_CODEC_SNAPPY: + return encode_snappy(c, data, len); +#endif +#ifdef DEFLATE_CODEC + case AVRO_CODEC_DEFLATE: + return encode_deflate(c, data, len); +#endif +#ifdef LZMA_CODEC + case AVRO_CODEC_LZMA: + return encode_lzma(c, data, len); +#endif + default: + return 1; + } +} + +int avro_codec_decode(avro_codec_t c, void * data, int64_t len) +{ + switch(c->type) + { + case AVRO_CODEC_NULL: + return decode_null(c, data, len); +#ifdef SNAPPY_CODEC + case AVRO_CODEC_SNAPPY: + return decode_snappy(c, data, len); +#endif +#ifdef DEFLATE_CODEC + case AVRO_CODEC_DEFLATE: + return decode_deflate(c, data, len); +#endif +#ifdef LZMA_CODEC + case AVRO_CODEC_LZMA: + return decode_lzma(c, data, len); +#endif + default: + return 1; + } +} + +int avro_codec_reset(avro_codec_t c) +{ + switch(c->type) + { + case AVRO_CODEC_NULL: + return reset_null(c); +#ifdef SNAPPY_CODEC + case AVRO_CODEC_SNAPPY: + return reset_snappy(c); +#endif +#ifdef DEFLATE_CODEC + case AVRO_CODEC_DEFLATE: + return reset_deflate(c); +#endif +#ifdef LZMA_CODEC + case AVRO_CODEC_LZMA: + return reset_lzma(c); +#endif + default: + return 1; + } +} diff --git a/fluent-bit/lib/avro/src/codec.h b/fluent-bit/lib/avro/src/codec.h new file mode 100644 index 000000000..689122430 --- /dev/null +++ b/fluent-bit/lib/avro/src/codec.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef AVRO_CODEC_H +#define AVRO_CODEC_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> + +enum avro_codec_type_t { + AVRO_CODEC_NULL, + AVRO_CODEC_DEFLATE, + AVRO_CODEC_LZMA, + AVRO_CODEC_SNAPPY +}; +typedef enum avro_codec_type_t avro_codec_type_t; + +struct avro_codec_t_ { + const char * name; + avro_codec_type_t type; + int64_t block_size; + int64_t used_size; + void * block_data; + void * codec_data; +}; +typedef struct avro_codec_t_* avro_codec_t; + +int avro_codec(avro_codec_t c, const char *type); +int avro_codec_reset(avro_codec_t c); +int avro_codec_encode(avro_codec_t c, void * data, int64_t len); +int avro_codec_decode(avro_codec_t c, void * data, int64_t len); + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/consume-binary.c b/fluent-bit/lib/avro/src/consume-binary.c new file mode 100644 index 000000000..9f92799d8 --- /dev/null +++ b/fluent-bit/lib/avro/src/consume-binary.c @@ -0,0 +1,328 @@ +/* + * 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_private.h" +#include "avro/allocation.h" +#include "avro/consumer.h" +#include "avro/errors.h" +#include "avro/resolver.h" +#include "avro/value.h" +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include "encoding.h" +#include "schema.h" +#include "datum.h" + + +static int +read_enum(avro_reader_t reader, const avro_encoding_t * enc, + avro_consumer_t *consumer, void *ud) +{ + int rval; + int64_t index; + + check_prefix(rval, enc->read_long(reader, &index), + "Cannot read enum value: "); + return avro_consumer_call(consumer, enum_value, index, ud); +} + +static int +read_array(avro_reader_t reader, const avro_encoding_t * enc, + avro_consumer_t *consumer, void *ud) +{ + int rval; + int64_t i; /* index within the current block */ + int64_t index = 0; /* index within the entire array */ + int64_t block_count; + int64_t block_size; + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read array block count: "); + check(rval, avro_consumer_call(consumer, array_start_block, + 1, block_count, ud)); + + while (block_count != 0) { + if (block_count < 0) { + block_count = block_count * -1; + check_prefix(rval, enc->read_long(reader, &block_size), + "Cannot read array block size: "); + } + + for (i = 0; i < block_count; i++, index++) { + avro_consumer_t *element_consumer = NULL; + void *element_ud = NULL; + + check(rval, + avro_consumer_call(consumer, array_element, + index, &element_consumer, &element_ud, + ud)); + + check(rval, avro_consume_binary(reader, element_consumer, element_ud)); + } + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read array block count: "); + check(rval, avro_consumer_call(consumer, array_start_block, + 0, block_count, ud)); + } + + return 0; +} + +static int +read_map(avro_reader_t reader, const avro_encoding_t * enc, + avro_consumer_t *consumer, void *ud) +{ + int rval; + int64_t i; /* index within the current block */ + int64_t index = 0; /* index within the entire array */ + int64_t block_count; + int64_t block_size; + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read map block count: "); + check(rval, avro_consumer_call(consumer, map_start_block, + 1, block_count, ud)); + + while (block_count != 0) { + if (block_count < 0) { + block_count = block_count * -1; + check_prefix(rval, enc->read_long(reader, &block_size), + "Cannot read map block size: "); + } + + for (i = 0; i < block_count; i++, index++) { + char *key; + int64_t key_size; + avro_consumer_t *element_consumer = NULL; + void *element_ud = NULL; + + check_prefix(rval, enc->read_string(reader, &key, &key_size), + "Cannot read map key: "); + + rval = avro_consumer_call(consumer, map_element, + index, key, + &element_consumer, &element_ud, + ud); + if (rval) { + avro_free(key, key_size); + return rval; + } + + rval = avro_consume_binary(reader, element_consumer, element_ud); + if (rval) { + avro_free(key, key_size); + return rval; + } + + avro_free(key, key_size); + } + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read map block count: "); + check(rval, avro_consumer_call(consumer, map_start_block, + 0, block_count, ud)); + } + + return 0; +} + +static int +read_union(avro_reader_t reader, const avro_encoding_t * enc, + avro_consumer_t *consumer, void *ud) +{ + int rval; + int64_t discriminant; + avro_consumer_t *branch_consumer = NULL; + void *branch_ud = NULL; + + check_prefix(rval, enc->read_long(reader, &discriminant), + "Cannot read union discriminant: "); + check(rval, avro_consumer_call(consumer, union_branch, + discriminant, + &branch_consumer, &branch_ud, ud)); + return avro_consume_binary(reader, branch_consumer, branch_ud); +} + +static int +read_record(avro_reader_t reader, const avro_encoding_t * enc, + avro_consumer_t *consumer, void *ud) +{ + int rval; + size_t num_fields; + unsigned int i; + + AVRO_UNUSED(enc); + + check(rval, avro_consumer_call(consumer, record_start, ud)); + + num_fields = avro_schema_record_size(consumer->schema); + for (i = 0; i < num_fields; i++) { + avro_consumer_t *field_consumer = NULL; + void *field_ud = NULL; + + check(rval, avro_consumer_call(consumer, record_field, + i, &field_consumer, &field_ud, + ud)); + + if (field_consumer) { + check(rval, avro_consume_binary(reader, field_consumer, field_ud)); + } else { + avro_schema_t field_schema = + avro_schema_record_field_get_by_index(consumer->schema, i); + check(rval, avro_skip_data(reader, field_schema)); + } + } + + return 0; +} + +int +avro_consume_binary(avro_reader_t reader, avro_consumer_t *consumer, void *ud) +{ + int rval; + const avro_encoding_t *enc = &avro_binary_encoding; + + check_param(EINVAL, reader, "reader"); + check_param(EINVAL, consumer, "consumer"); + + switch (avro_typeof(consumer->schema)) { + case AVRO_NULL: + check_prefix(rval, enc->read_null(reader), + "Cannot read null value: "); + check(rval, avro_consumer_call(consumer, null_value, ud)); + break; + + case AVRO_BOOLEAN: + { + int8_t b; + check_prefix(rval, enc->read_boolean(reader, &b), + "Cannot read boolean value: "); + check(rval, avro_consumer_call(consumer, boolean_value, b, ud)); + } + break; + + case AVRO_STRING: + { + int64_t len; + char *s; + check_prefix(rval, enc->read_string(reader, &s, &len), + "Cannot read string value: "); + check(rval, avro_consumer_call(consumer, string_value, s, len, ud)); + } + break; + + case AVRO_INT32: + { + int32_t i; + check_prefix(rval, enc->read_int(reader, &i), + "Cannot read int value: "); + check(rval, avro_consumer_call(consumer, int_value, i, ud)); + } + break; + + case AVRO_INT64: + { + int64_t l; + check_prefix(rval, enc->read_long(reader, &l), + "Cannot read long value: "); + check(rval, avro_consumer_call(consumer, long_value, l, ud)); + } + break; + + case AVRO_FLOAT: + { + float f; + check_prefix(rval, enc->read_float(reader, &f), + "Cannot read float value: "); + check(rval, avro_consumer_call(consumer, float_value, f, ud)); + } + break; + + case AVRO_DOUBLE: + { + double d; + check_prefix(rval, enc->read_double(reader, &d), + "Cannot read double value: "); + check(rval, avro_consumer_call(consumer, double_value, d, ud)); + } + break; + + case AVRO_BYTES: + { + char *bytes; + int64_t len; + check_prefix(rval, enc->read_bytes(reader, &bytes, &len), + "Cannot read bytes value: "); + check(rval, avro_consumer_call(consumer, bytes_value, bytes, len, ud)); + } + break; + + case AVRO_FIXED: + { + char *bytes; + int64_t size = + avro_schema_to_fixed(consumer->schema)->size; + + bytes = (char *) avro_malloc(size); + if (!bytes) { + avro_prefix_error("Cannot allocate new fixed value"); + return ENOMEM; + } + rval = avro_read(reader, bytes, size); + if (rval) { + avro_prefix_error("Cannot read fixed value: "); + avro_free(bytes, size); + return rval; + } + + rval = avro_consumer_call(consumer, fixed_value, bytes, size, ud); + if (rval) { + avro_free(bytes, size); + return rval; + } + } + break; + + case AVRO_ENUM: + check(rval, read_enum(reader, enc, consumer, ud)); + break; + + case AVRO_ARRAY: + check(rval, read_array(reader, enc, consumer, ud)); + break; + + case AVRO_MAP: + check(rval, read_map(reader, enc, consumer, ud)); + break; + + case AVRO_UNION: + check(rval, read_union(reader, enc, consumer, ud)); + break; + + case AVRO_RECORD: + check(rval, read_record(reader, enc, consumer, ud)); + break; + + case AVRO_LINK: + avro_set_error("Consumer can't consume a link schema directly"); + return EINVAL; + } + + return 0; +} diff --git a/fluent-bit/lib/avro/src/consumer.c b/fluent-bit/lib/avro/src/consumer.c new file mode 100644 index 000000000..e01958c8b --- /dev/null +++ b/fluent-bit/lib/avro/src/consumer.c @@ -0,0 +1,23 @@ +/* + * 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/consumer.h" + +void avro_consumer_free(avro_consumer_t *consumer) +{ + consumer->free(consumer); +} diff --git a/fluent-bit/lib/avro/src/datafile.c b/fluent-bit/lib/avro/src/datafile.c new file mode 100644 index 000000000..c9d4dfeb6 --- /dev/null +++ b/fluent-bit/lib/avro/src/datafile.c @@ -0,0 +1,766 @@ +/* + * 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_private.h" +#include "avro/allocation.h" +#include "avro/generic.h" +#include "avro/errors.h" +#include "avro/value.h" +#include "encoding.h" +#include "codec.h" +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#include <string.h> + +struct avro_file_reader_t_ { + avro_schema_t writers_schema; + avro_reader_t reader; + avro_reader_t block_reader; + avro_codec_t codec; + char sync[16]; + int64_t blocks_read; + int64_t blocks_total; + int64_t current_blocklen; + char * current_blockdata; +}; + +struct avro_file_writer_t_ { + avro_schema_t writers_schema; + avro_writer_t writer; + avro_codec_t codec; + char sync[16]; + int block_count; + size_t block_size; + avro_writer_t datum_writer; + char* datum_buffer; + size_t datum_buffer_size; + char schema_buf[64 * 1024]; +}; + +#define DEFAULT_BLOCK_SIZE 16 * 1024 + +/* Note: We should not just read /dev/random here, because it may not + * exist on all platforms e.g. Win32. + */ +static void generate_sync(avro_file_writer_t w) +{ + unsigned int i; + srand(time(NULL)); + for (i = 0; i < sizeof(w->sync); i++) { + w->sync[i] = ((double)rand() / (RAND_MAX + 1.0)) * 255; + } +} + +static int write_sync(avro_file_writer_t w) +{ + return avro_write(w->writer, w->sync, sizeof(w->sync)); +} + +static int write_header(avro_file_writer_t w) +{ + int rval; + uint8_t version = 1; + /* TODO: remove this static buffer */ + avro_writer_t schema_writer; + const avro_encoding_t *enc = &avro_binary_encoding; + int64_t schema_len; + + /* Generate random sync */ + generate_sync(w); + + check(rval, avro_write(w->writer, "Obj", 3)); + check(rval, avro_write(w->writer, &version, 1)); + + check(rval, enc->write_long(w->writer, 2)); + check(rval, enc->write_string(w->writer, "avro.codec")); + check(rval, enc->write_bytes(w->writer, w->codec->name, strlen(w->codec->name))); + check(rval, enc->write_string(w->writer, "avro.schema")); + schema_writer = + avro_writer_memory(&w->schema_buf[0], sizeof(w->schema_buf)); + rval = avro_schema_to_json(w->writers_schema, schema_writer); + if (rval) { + avro_writer_free(schema_writer); + return rval; + } + schema_len = avro_writer_tell(schema_writer); + avro_writer_free(schema_writer); + check(rval, + enc->write_bytes(w->writer, w->schema_buf, schema_len)); + check(rval, enc->write_long(w->writer, 0)); + return write_sync(w); +} + +static int +file_writer_init_fp(FILE *fp, const char *path, int should_close, const char *mode, avro_file_writer_t w) +{ + if (!fp) { + fp = fopen(path, mode); + } + + if (!fp) { + avro_set_error("Cannot open file for %s", path); + return ENOMEM; + } + w->writer = avro_writer_file_fp(fp, should_close); + if (!w->writer) { + if (should_close) { + fclose(fp); + } + avro_set_error("Cannot create file writer for %s", path); + return ENOMEM; + } + return 0; +} + +/* Exclusive file writing is supported by GCC using the mode + * "wx". Win32 does not support exclusive file writing, so for win32 + * fall back to the non-exclusive file writing. + */ +#ifdef _WIN32 + #define EXCLUSIVE_WRITE_MODE "wb" +#else + #define EXCLUSIVE_WRITE_MODE "wbx" +#endif + +static int +file_writer_create(FILE *fp, const char *path, int should_close, avro_schema_t schema, avro_file_writer_t w, size_t block_size) +{ + int rval; + + w->block_count = 0; + rval = file_writer_init_fp(fp, path, should_close, EXCLUSIVE_WRITE_MODE, w); + if (rval) { + check(rval, file_writer_init_fp(fp, path, should_close, "wb", w)); + } + + w->datum_buffer_size = block_size; + w->datum_buffer = (char *) avro_malloc(w->datum_buffer_size); + + if(!w->datum_buffer) { + avro_set_error("Could not allocate datum buffer\n"); + avro_writer_free(w->writer); + return ENOMEM; + } + + w->datum_writer = + avro_writer_memory(w->datum_buffer, w->datum_buffer_size); + if (!w->datum_writer) { + avro_set_error("Cannot create datum writer for file %s", path); + avro_writer_free(w->writer); + avro_free(w->datum_buffer, w->datum_buffer_size); + return ENOMEM; + } + + w->writers_schema = avro_schema_incref(schema); + return write_header(w); +} + +int +avro_file_writer_create(const char *path, avro_schema_t schema, + avro_file_writer_t * writer) +{ + return avro_file_writer_create_with_codec_fp(NULL, path, 1, schema, writer, "null", 0); +} + +int +avro_file_writer_create_fp(FILE *fp, const char *path, int should_close, avro_schema_t schema, + avro_file_writer_t * writer) +{ + return avro_file_writer_create_with_codec_fp(fp, path, should_close, schema, writer, "null", 0); +} + +int avro_file_writer_create_with_codec(const char *path, + avro_schema_t schema, avro_file_writer_t * writer, + const char *codec, size_t block_size) +{ + return avro_file_writer_create_with_codec_fp(NULL, path, 1, schema, writer, codec, block_size); +} + +int avro_file_writer_create_with_codec_fp(FILE *fp, const char *path, int should_close, + avro_schema_t schema, avro_file_writer_t * writer, + const char *codec, size_t block_size) +{ + avro_file_writer_t w; + int rval; + check_param(EINVAL, path, "path"); + check_param(EINVAL, is_avro_schema(schema), "schema"); + check_param(EINVAL, writer, "writer"); + check_param(EINVAL, codec, "codec"); + + if (block_size == 0) { + block_size = DEFAULT_BLOCK_SIZE; + } + + w = (avro_file_writer_t) avro_new(struct avro_file_writer_t_); + if (!w) { + avro_set_error("Cannot allocate new file writer"); + return ENOMEM; + } + w->codec = (avro_codec_t) avro_new(struct avro_codec_t_); + if (!w->codec) { + avro_set_error("Cannot allocate new codec"); + avro_freet(struct avro_file_writer_t_, w); + return ENOMEM; + } + rval = avro_codec(w->codec, codec); + if (rval) { + avro_codec_reset(w->codec); + avro_freet(struct avro_codec_t_, w->codec); + avro_freet(struct avro_file_writer_t_, w); + return rval; + } + rval = file_writer_create(fp, path, should_close, schema, w, block_size); + if (rval) { + avro_codec_reset(w->codec); + avro_freet(struct avro_codec_t_, w->codec); + avro_freet(struct avro_file_writer_t_, w); + return rval; + } + *writer = w; + + return 0; +} + +static int file_read_header(avro_reader_t reader, + avro_schema_t * writers_schema, avro_codec_t codec, + char *sync, int synclen) +{ + int rval; + avro_schema_t meta_schema; + avro_schema_t meta_values_schema; + avro_value_iface_t *meta_iface; + avro_value_t meta; + char magic[4]; + avro_value_t codec_val; + avro_value_t schema_bytes; + const void *p; + size_t len; + + check(rval, avro_read(reader, magic, sizeof(magic))); + if (magic[0] != 'O' || magic[1] != 'b' || magic[2] != 'j' + || magic[3] != 1) { + avro_set_error("Incorrect Avro container file magic number"); + return EILSEQ; + } + + meta_values_schema = avro_schema_bytes(); + meta_schema = avro_schema_map(meta_values_schema); + meta_iface = avro_generic_class_from_schema(meta_schema); + if (meta_iface == NULL) { + return EILSEQ; + } + check(rval, avro_generic_value_new(meta_iface, &meta)); + rval = avro_value_read(reader, &meta); + if (rval) { + avro_prefix_error("Cannot read file header: "); + return EILSEQ; + } + avro_schema_decref(meta_schema); + + rval = avro_value_get_by_name(&meta, "avro.codec", &codec_val, NULL); + if (rval) { + if (avro_codec(codec, NULL) != 0) { + avro_set_error("Codec not specified in header and unable to set 'null' codec"); + avro_value_decref(&meta); + return EILSEQ; + } + } else { + const void *buf; + size_t size; + char codec_name[11]; + + avro_type_t type = avro_value_get_type(&codec_val); + + if (type != AVRO_BYTES) { + avro_set_error("Value type of codec is unexpected"); + avro_value_decref(&meta); + return EILSEQ; + } + + avro_value_get_bytes(&codec_val, &buf, &size); + memset(codec_name, 0, sizeof(codec_name)); + strncpy(codec_name, (const char *) buf, size < 10 ? size : 10); + + if (avro_codec(codec, codec_name) != 0) { + avro_set_error("File header contains an unknown codec"); + avro_value_decref(&meta); + return EILSEQ; + } + } + + rval = avro_value_get_by_name(&meta, "avro.schema", &schema_bytes, NULL); + if (rval) { + avro_set_error("File header doesn't contain a schema"); + avro_value_decref(&meta); + return EILSEQ; + } + + avro_value_get_bytes(&schema_bytes, &p, &len); + rval = avro_schema_from_json_length((const char *) p, len, writers_schema); + if (rval) { + avro_prefix_error("Cannot parse file header: "); + avro_value_decref(&meta); + return rval; + } + + avro_value_decref(&meta); + avro_value_iface_decref(meta_iface); + return avro_read(reader, sync, synclen); +} + +static int +file_writer_open(const char *path, avro_file_writer_t w, size_t block_size) +{ + int rval; + FILE *fp; + avro_reader_t reader; + + /* Open for read AND write */ + fp = fopen(path, "r+b"); + if (!fp) { + avro_set_error("Error opening file: %s", + strerror(errno)); + return errno; + } + + /* Don`t close the underlying file descriptor, logrotate can + * vanish it from sight. */ + reader = avro_reader_file_fp(fp, 0); + if (!reader) { + fclose(fp); + avro_set_error("Cannot create file reader for %s", path); + return ENOMEM; + } + rval = + file_read_header(reader, &w->writers_schema, w->codec, w->sync, + sizeof(w->sync)); + + avro_reader_free(reader); + if (rval) { + fclose(fp); + return rval; + } + + w->block_count = 0; + + /* Position to end of file and get ready to write */ + fseek(fp, 0, SEEK_END); + + w->writer = avro_writer_file(fp); + if (!w->writer) { + fclose(fp); + avro_set_error("Cannot create file writer for %s", path); + return ENOMEM; + } + + if (block_size == 0) { + block_size = DEFAULT_BLOCK_SIZE; + } + + w->datum_buffer_size = block_size; + w->datum_buffer = (char *) avro_malloc(w->datum_buffer_size); + + if(!w->datum_buffer) { + avro_set_error("Could not allocate datum buffer\n"); + avro_writer_free(w->writer); + return ENOMEM; + } + + w->datum_writer = + avro_writer_memory(w->datum_buffer, w->datum_buffer_size); + if (!w->datum_writer) { + avro_set_error("Cannot create datum writer for file %s", path); + avro_writer_free(w->writer); + avro_free(w->datum_buffer, w->datum_buffer_size); + return ENOMEM; + } + + return 0; +} + +int +avro_file_writer_open_bs(const char *path, avro_file_writer_t * writer, + size_t block_size) +{ + avro_file_writer_t w; + int rval; + check_param(EINVAL, path, "path"); + check_param(EINVAL, writer, "writer"); + + w = (avro_file_writer_t) avro_new(struct avro_file_writer_t_); + if (!w) { + avro_set_error("Cannot create new file writer for %s", path); + return ENOMEM; + } + w->codec = (avro_codec_t) avro_new(struct avro_codec_t_); + if (!w->codec) { + avro_set_error("Cannot allocate new codec"); + avro_freet(struct avro_file_writer_t_, w); + return ENOMEM; + } + avro_codec(w->codec, NULL); + rval = file_writer_open(path, w, block_size); + if (rval) { + avro_codec_reset(w->codec); + avro_freet(struct avro_codec_t_, w->codec); + avro_freet(struct avro_file_writer_t_, w); + return rval; + } + + *writer = w; + return 0; +} + +int +avro_file_writer_open(const char *path, avro_file_writer_t * writer) +{ + return avro_file_writer_open_bs(path, writer, 0); +} + +static int file_read_block_count(avro_file_reader_t r) +{ + int rval; + int64_t len; + const avro_encoding_t *enc = &avro_binary_encoding; + + /* For a correctly formatted file, EOF will occur here */ + rval = enc->read_long(r->reader, &r->blocks_total); + + if (rval == EILSEQ && avro_reader_is_eof(r->reader)) { + return EOF; + } + + check_prefix(rval, rval, + "Cannot read file block count: "); + check_prefix(rval, enc->read_long(r->reader, &len), + "Cannot read file block size: "); + + if (r->current_blockdata && len > r->current_blocklen) { + r->current_blockdata = (char *) avro_realloc(r->current_blockdata, r->current_blocklen, len); + r->current_blocklen = len; + } else if (!r->current_blockdata) { + r->current_blockdata = (char *) avro_malloc(len); + r->current_blocklen = len; + } + + if (len > 0) { + check_prefix(rval, avro_read(r->reader, r->current_blockdata, len), + "Cannot read file block: "); + + check_prefix(rval, avro_codec_decode(r->codec, r->current_blockdata, len), + "Cannot decode file block: "); + } + + avro_reader_memory_set_source(r->block_reader, (const char *) r->codec->block_data, r->codec->used_size); + + r->blocks_read = 0; + return 0; +} + +int avro_file_reader_fp(FILE *fp, const char *path, int should_close, + avro_file_reader_t * reader) +{ + int rval; + avro_file_reader_t r = (avro_file_reader_t) avro_new(struct avro_file_reader_t_); + if (!r) { + if (should_close) { + fclose(fp); + } + avro_set_error("Cannot allocate file reader for %s", path); + return ENOMEM; + } + + r->reader = avro_reader_file_fp(fp, should_close); + if (!r->reader) { + if (should_close) { + fclose(fp); + } + avro_set_error("Cannot allocate reader for file %s", path); + avro_freet(struct avro_file_reader_t_, r); + return ENOMEM; + } + r->block_reader = avro_reader_memory(0, 0); + if (!r->block_reader) { + avro_set_error("Cannot allocate block reader for file %s", path); + avro_reader_free(r->reader); + avro_freet(struct avro_file_reader_t_, r); + return ENOMEM; + } + + r->codec = (avro_codec_t) avro_new(struct avro_codec_t_); + if (!r->codec) { + avro_set_error("Could not allocate codec for file %s", path); + avro_reader_free(r->reader); + avro_freet(struct avro_file_reader_t_, r); + return ENOMEM; + } + avro_codec(r->codec, NULL); + + rval = file_read_header(r->reader, &r->writers_schema, r->codec, + r->sync, sizeof(r->sync)); + if (rval) { + avro_reader_free(r->reader); + avro_codec_reset(r->codec); + avro_freet(struct avro_codec_t_, r->codec); + avro_freet(struct avro_file_reader_t_, r); + return rval; + } + + r->current_blockdata = NULL; + r->current_blocklen = 0; + + rval = file_read_block_count(r); + if (rval == EOF) { + r->blocks_total = 0; + } else if (rval) { + avro_reader_free(r->reader); + avro_codec_reset(r->codec); + avro_freet(struct avro_codec_t_, r->codec); + avro_freet(struct avro_file_reader_t_, r); + return rval; + } + + *reader = r; + return 0; +} + +int avro_file_reader(const char *path, avro_file_reader_t * reader) +{ + FILE *fp; + + fp = fopen(path, "rb"); + if (!fp) { + return errno; + } + + return avro_file_reader_fp(fp, path, 1, reader); +} + +avro_schema_t +avro_file_reader_get_writer_schema(avro_file_reader_t r) +{ + check_param(NULL, r, "reader"); + return avro_schema_incref(r->writers_schema); +} + +static int file_write_block(avro_file_writer_t w) +{ + const avro_encoding_t *enc = &avro_binary_encoding; + int rval; + + if (w->block_count) { + /* Write the block count */ + check_prefix(rval, enc->write_long(w->writer, w->block_count), + "Cannot write file block count: "); + /* Encode the block */ + check_prefix(rval, avro_codec_encode(w->codec, w->datum_buffer, w->block_size), + "Cannot encode file block: "); + /* Write the block length */ + check_prefix(rval, enc->write_long(w->writer, w->codec->used_size), + "Cannot write file block size: "); + /* Write the block */ + check_prefix(rval, avro_write(w->writer, w->codec->block_data, w->codec->used_size), + "Cannot write file block: "); + /* Write the sync marker */ + check_prefix(rval, write_sync(w), + "Cannot write sync marker: "); + /* Reset the datum writer */ + avro_writer_reset(w->datum_writer); + w->block_count = 0; + w->block_size = 0; + } + return 0; +} + +int avro_file_writer_append(avro_file_writer_t w, avro_datum_t datum) +{ + int rval; + check_param(EINVAL, w, "writer"); + check_param(EINVAL, datum, "datum"); + + rval = avro_write_data(w->datum_writer, w->writers_schema, datum); + if (rval) { + check(rval, file_write_block(w)); + rval = + avro_write_data(w->datum_writer, w->writers_schema, datum); + if (rval) { + avro_set_error("Datum too large for file block size"); + /* TODO: if the datum encoder larger than our buffer, + just write a single large datum */ + return rval; + } + } + w->block_count++; + w->block_size = avro_writer_tell(w->datum_writer); + return 0; +} + +int +avro_file_writer_append_value(avro_file_writer_t w, avro_value_t *value) +{ + int rval; + check_param(EINVAL, w, "writer"); + check_param(EINVAL, value, "value"); + + rval = avro_value_write(w->datum_writer, value); + if (rval) { + check(rval, file_write_block(w)); + rval = avro_value_write(w->datum_writer, value); + if (rval) { + avro_set_error("Value too large for file block size"); + /* TODO: if the value encoder larger than our buffer, + just write a single large datum */ + return rval; + } + } + w->block_count++; + w->block_size = avro_writer_tell(w->datum_writer); + return 0; +} + +int +avro_file_writer_append_encoded(avro_file_writer_t w, + const void *buf, int64_t len) +{ + int rval; + check_param(EINVAL, w, "writer"); + + rval = avro_write(w->datum_writer, (void *) buf, len); + if (rval) { + check(rval, file_write_block(w)); + rval = avro_write(w->datum_writer, (void *) buf, len); + if (rval) { + avro_set_error("Value too large for file block size"); + /* TODO: if the value encoder larger than our buffer, + just write a single large datum */ + return rval; + } + } + w->block_count++; + w->block_size = avro_writer_tell(w->datum_writer); + return 0; +} + +int avro_file_writer_sync(avro_file_writer_t w) +{ + return file_write_block(w); +} + +int avro_file_writer_flush(avro_file_writer_t w) +{ + int rval; + check(rval, file_write_block(w)); + avro_writer_flush(w->writer); + return 0; +} + +int avro_file_writer_close(avro_file_writer_t w) +{ + int rval; + check(rval, avro_file_writer_flush(w)); + avro_schema_decref(w->writers_schema); + avro_writer_free(w->datum_writer); + avro_writer_free(w->writer); + avro_free(w->datum_buffer, w->datum_buffer_size); + avro_codec_reset(w->codec); + avro_freet(struct avro_codec_t_, w->codec); + avro_freet(struct avro_file_writer_t_, w); + return 0; +} + +int avro_file_reader_read(avro_file_reader_t r, avro_schema_t readers_schema, + avro_datum_t * datum) +{ + int rval; + char sync[16]; + + check_param(EINVAL, r, "reader"); + check_param(EINVAL, datum, "datum"); + + /* This will be set to zero when an empty file is opened. + * Return EOF here when the user attempts to read. */ + if (r->blocks_total == 0) { + return EOF; + } + + if (r->blocks_read == r->blocks_total) { + check(rval, avro_read(r->reader, sync, sizeof(sync))); + if (memcmp(r->sync, sync, sizeof(r->sync)) != 0) { + /* wrong sync bytes */ + avro_set_error("Incorrect sync bytes"); + return EILSEQ; + } + check(rval, file_read_block_count(r)); + } + + check(rval, + avro_read_data(r->block_reader, r->writers_schema, readers_schema, + datum)); + r->blocks_read++; + + return 0; +} + +int +avro_file_reader_read_value(avro_file_reader_t r, avro_value_t *value) +{ + int rval; + char sync[16]; + + check_param(EINVAL, r, "reader"); + check_param(EINVAL, value, "value"); + + /* This will be set to zero when an empty file is opened. + * Return EOF here when the user attempts to read. */ + if (r->blocks_total == 0) { + return EOF; + } + + if (r->blocks_read == r->blocks_total) { + /* reads sync bytes and buffers further bytes */ + check(rval, avro_read(r->reader, sync, sizeof(sync))); + if (memcmp(r->sync, sync, sizeof(r->sync)) != 0) { + /* wrong sync bytes */ + avro_set_error("Incorrect sync bytes"); + return EILSEQ; + } + + check(rval, file_read_block_count(r)); + } + + check(rval, avro_value_read(r->block_reader, value)); + r->blocks_read++; + + return 0; +} + +int avro_file_reader_close(avro_file_reader_t reader) +{ + avro_schema_decref(reader->writers_schema); + avro_reader_free(reader->reader); + avro_reader_free(reader->block_reader); + avro_codec_reset(reader->codec); + avro_freet(struct avro_codec_t_, reader->codec); + if (reader->current_blockdata) { + avro_free(reader->current_blockdata, reader->current_blocklen); + } + avro_freet(struct avro_file_reader_t_, reader); + return 0; +} diff --git a/fluent-bit/lib/avro/src/datum.c b/fluent-bit/lib/avro/src/datum.c new file mode 100644 index 000000000..2c4278090 --- /dev/null +++ b/fluent-bit/lib/avro/src/datum.c @@ -0,0 +1,1255 @@ +/* + * 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/basics.h" +#include "avro/errors.h" +#include "avro/legacy.h" +#include "avro/refcount.h" +#include "avro/schema.h" +#include "avro_private.h" +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "datum.h" +#include "schema.h" +#include "encoding.h" + +#define DEFAULT_TABLE_SIZE 32 + +static void avro_datum_init(avro_datum_t datum, avro_type_t type) +{ + datum->type = type; + datum->class_type = AVRO_DATUM; + avro_refcount_set(&datum->refcount, 1); +} + +static void +avro_str_free_wrapper(void *ptr, size_t sz) +{ + // don't need sz, since the size is stored in the string buffer + AVRO_UNUSED(sz); + avro_str_free((char *)ptr); +} + +static avro_datum_t avro_string_private(char *str, int64_t size, + avro_free_func_t string_free) +{ + struct avro_string_datum_t *datum = + (struct avro_string_datum_t *) avro_new(struct avro_string_datum_t); + if (!datum) { + avro_set_error("Cannot create new string datum"); + return NULL; + } + datum->s = str; + datum->size = size; + datum->free = string_free; + + avro_datum_init(&datum->obj, AVRO_STRING); + return &datum->obj; +} + +avro_datum_t avro_string(const char *str) +{ + char *p = avro_strdup(str); + if (!p) { + avro_set_error("Cannot copy string content"); + return NULL; + } + avro_datum_t s_datum = avro_string_private(p, 0, avro_str_free_wrapper); + if (!s_datum) { + avro_str_free(p); + } + + return s_datum; +} + +avro_datum_t avro_givestring(const char *str, + avro_free_func_t free) +{ + int64_t sz = strlen(str)+1; + return avro_string_private((char *)str, sz, free); +} + +int avro_string_get(avro_datum_t datum, char **p) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_string(datum), "string datum"); + check_param(EINVAL, p, "string buffer"); + + *p = avro_datum_to_string(datum)->s; + return 0; +} + +static int avro_string_set_private(avro_datum_t datum, + const char *p, int64_t size, + avro_free_func_t string_free) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_string(datum), "string datum"); + check_param(EINVAL, p, "string content"); + + struct avro_string_datum_t *string = avro_datum_to_string(datum); + if (string->free) { + string->free(string->s, string->size); + } + string->free = string_free; + string->s = (char *)p; + string->size = size; + return 0; +} + +int avro_string_set(avro_datum_t datum, const char *p) +{ + char *string_copy = avro_strdup(p); + int rval; + if (!string_copy) { + avro_set_error("Cannot copy string content"); + return ENOMEM; + } + rval = avro_string_set_private(datum, string_copy, 0, + avro_str_free_wrapper); + if (rval) { + avro_str_free(string_copy); + } + return rval; +} + +int avro_givestring_set(avro_datum_t datum, const char *p, + avro_free_func_t free) +{ + int64_t size = strlen(p)+1; + return avro_string_set_private(datum, p, size, free); +} + +static avro_datum_t avro_bytes_private(char *bytes, int64_t size, + avro_free_func_t bytes_free) +{ + struct avro_bytes_datum_t *datum; + datum = (struct avro_bytes_datum_t *) avro_new(struct avro_bytes_datum_t); + if (!datum) { + avro_set_error("Cannot create new bytes datum"); + return NULL; + } + datum->bytes = bytes; + datum->size = size; + datum->free = bytes_free; + + avro_datum_init(&datum->obj, AVRO_BYTES); + return &datum->obj; +} + +avro_datum_t avro_bytes(const char *bytes, int64_t size) +{ + char *bytes_copy = (char *) avro_malloc(size); + if (!bytes_copy) { + avro_set_error("Cannot copy bytes content"); + return NULL; + } + memcpy(bytes_copy, bytes, size); + avro_datum_t result = + avro_bytes_private(bytes_copy, size, avro_alloc_free_func); + if (result == NULL) { + avro_free(bytes_copy, size); + } + return result; +} + +avro_datum_t avro_givebytes(const char *bytes, int64_t size, + avro_free_func_t free) +{ + return avro_bytes_private((char *)bytes, size, free); +} + +static int avro_bytes_set_private(avro_datum_t datum, const char *bytes, + const int64_t size, + avro_free_func_t bytes_free) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_bytes(datum), "bytes datum"); + + struct avro_bytes_datum_t *b = avro_datum_to_bytes(datum); + if (b->free) { + b->free(b->bytes, b->size); + } + + b->free = bytes_free; + b->bytes = (char *)bytes; + b->size = size; + return 0; +} + +int avro_bytes_set(avro_datum_t datum, const char *bytes, const int64_t size) +{ + int rval; + char *bytes_copy = (char *) avro_malloc(size); + if (!bytes_copy) { + avro_set_error("Cannot copy bytes content"); + return ENOMEM; + } + memcpy(bytes_copy, bytes, size); + rval = avro_bytes_set_private(datum, bytes_copy, size, avro_alloc_free_func); + if (rval) { + avro_free(bytes_copy, size); + } + return rval; +} + +int avro_givebytes_set(avro_datum_t datum, const char *bytes, + const int64_t size, avro_free_func_t free) +{ + return avro_bytes_set_private(datum, bytes, size, free); +} + +int avro_bytes_get(avro_datum_t datum, char **bytes, int64_t * size) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_bytes(datum), "bytes datum"); + check_param(EINVAL, bytes, "bytes"); + check_param(EINVAL, size, "size"); + + *bytes = avro_datum_to_bytes(datum)->bytes; + *size = avro_datum_to_bytes(datum)->size; + return 0; +} + +avro_datum_t avro_int32(int32_t i) +{ + struct avro_int32_datum_t *datum = + (struct avro_int32_datum_t *) avro_new(struct avro_int32_datum_t); + if (!datum) { + avro_set_error("Cannot create new int datum"); + return NULL; + } + datum->i32 = i; + + avro_datum_init(&datum->obj, AVRO_INT32); + return &datum->obj; +} + +int avro_int32_get(avro_datum_t datum, int32_t * i) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_int32(datum), "int datum"); + check_param(EINVAL, i, "value pointer"); + + *i = avro_datum_to_int32(datum)->i32; + return 0; +} + +int avro_int32_set(avro_datum_t datum, const int32_t i) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_int32(datum), "int datum"); + + avro_datum_to_int32(datum)->i32 = i; + return 0; +} + +avro_datum_t avro_int64(int64_t l) +{ + struct avro_int64_datum_t *datum = + (struct avro_int64_datum_t *) avro_new(struct avro_int64_datum_t); + if (!datum) { + avro_set_error("Cannot create new long datum"); + return NULL; + } + datum->i64 = l; + + avro_datum_init(&datum->obj, AVRO_INT64); + return &datum->obj; +} + +int avro_int64_get(avro_datum_t datum, int64_t * l) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_int64(datum), "long datum"); + check_param(EINVAL, l, "value pointer"); + + *l = avro_datum_to_int64(datum)->i64; + return 0; +} + +int avro_int64_set(avro_datum_t datum, const int64_t l) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_int64(datum), "long datum"); + + avro_datum_to_int64(datum)->i64 = l; + return 0; +} + +avro_datum_t avro_float(float f) +{ + struct avro_float_datum_t *datum = + (struct avro_float_datum_t *) avro_new(struct avro_float_datum_t); + if (!datum) { + avro_set_error("Cannot create new float datum"); + return NULL; + } + datum->f = f; + + avro_datum_init(&datum->obj, AVRO_FLOAT); + return &datum->obj; +} + +int avro_float_set(avro_datum_t datum, const float f) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_float(datum), "float datum"); + + avro_datum_to_float(datum)->f = f; + return 0; +} + +int avro_float_get(avro_datum_t datum, float *f) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_float(datum), "float datum"); + check_param(EINVAL, f, "value pointer"); + + *f = avro_datum_to_float(datum)->f; + return 0; +} + +avro_datum_t avro_double(double d) +{ + struct avro_double_datum_t *datum = + (struct avro_double_datum_t *) avro_new(struct avro_double_datum_t); + if (!datum) { + avro_set_error("Cannot create new double atom"); + return NULL; + } + datum->d = d; + + avro_datum_init(&datum->obj, AVRO_DOUBLE); + return &datum->obj; +} + +int avro_double_set(avro_datum_t datum, const double d) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_double(datum), "double datum"); + + avro_datum_to_double(datum)->d = d; + return 0; +} + +int avro_double_get(avro_datum_t datum, double *d) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_double(datum), "double datum"); + check_param(EINVAL, d, "value pointer"); + + *d = avro_datum_to_double(datum)->d; + return 0; +} + +avro_datum_t avro_boolean(int8_t i) +{ + struct avro_boolean_datum_t *datum = + (struct avro_boolean_datum_t *) avro_new(struct avro_boolean_datum_t); + if (!datum) { + avro_set_error("Cannot create new boolean datum"); + return NULL; + } + datum->i = i; + avro_datum_init(&datum->obj, AVRO_BOOLEAN); + return &datum->obj; +} + +int avro_boolean_set(avro_datum_t datum, const int8_t i) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_boolean(datum), "boolean datum"); + + avro_datum_to_boolean(datum)->i = i; + return 0; +} + +int avro_boolean_get(avro_datum_t datum, int8_t * i) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_boolean(datum), "boolean datum"); + check_param(EINVAL, i, "value pointer"); + + *i = avro_datum_to_boolean(datum)->i; + return 0; +} + +avro_datum_t avro_null(void) +{ + static struct avro_obj_t obj = { + AVRO_NULL, + AVRO_DATUM, + 1 + }; + return avro_datum_incref(&obj); +} + +avro_datum_t avro_union(avro_schema_t schema, + int64_t discriminant, avro_datum_t value) +{ + check_param(NULL, is_avro_schema(schema), "schema"); + + struct avro_union_datum_t *datum = + (struct avro_union_datum_t *) avro_new(struct avro_union_datum_t); + if (!datum) { + avro_set_error("Cannot create new union datum"); + return NULL; + } + datum->schema = avro_schema_incref(schema); + datum->discriminant = discriminant; + datum->value = avro_datum_incref(value); + + avro_datum_init(&datum->obj, AVRO_UNION); + return &datum->obj; +} + +int64_t avro_union_discriminant(const avro_datum_t datum) +{ + return avro_datum_to_union(datum)->discriminant; +} + +avro_datum_t avro_union_current_branch(avro_datum_t datum) +{ + return avro_datum_to_union(datum)->value; +} + +int avro_union_set_discriminant(avro_datum_t datum, + int discriminant, + avro_datum_t *branch) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_union(datum), "union datum"); + + struct avro_union_datum_t *unionp = avro_datum_to_union(datum); + + avro_schema_t schema = unionp->schema; + avro_schema_t branch_schema = + avro_schema_union_branch(schema, discriminant); + + if (branch_schema == NULL) { + // That branch doesn't exist! + avro_set_error("Branch %d doesn't exist", discriminant); + return EINVAL; + } + + if (unionp->discriminant != discriminant) { + // If we're changing the branch, throw away any old + // branch value. + if (unionp->value != NULL) { + avro_datum_decref(unionp->value); + unionp->value = NULL; + } + + unionp->discriminant = discriminant; + } + + // Create a new branch value, if there isn't one already. + if (unionp->value == NULL) { + unionp->value = avro_datum_from_schema(branch_schema); + } + + if (branch != NULL) { + *branch = unionp->value; + } + + return 0; +} + +avro_datum_t avro_record(avro_schema_t schema) +{ + check_param(NULL, is_avro_schema(schema), "schema"); + + struct avro_record_datum_t *datum = + (struct avro_record_datum_t *) avro_new(struct avro_record_datum_t); + if (!datum) { + avro_set_error("Cannot create new record datum"); + return NULL; + } + datum->field_order = st_init_numtable_with_size(DEFAULT_TABLE_SIZE); + if (!datum->field_order) { + avro_set_error("Cannot create new record datum"); + avro_freet(struct avro_record_datum_t, datum); + return NULL; + } + datum->fields_byname = st_init_strtable_with_size(DEFAULT_TABLE_SIZE); + if (!datum->fields_byname) { + avro_set_error("Cannot create new record datum"); + st_free_table(datum->field_order); + avro_freet(struct avro_record_datum_t, datum); + return NULL; + } + + datum->schema = avro_schema_incref(schema); + avro_datum_init(&datum->obj, AVRO_RECORD); + return &datum->obj; +} + +int +avro_record_get(const avro_datum_t datum, const char *field_name, + avro_datum_t * field) +{ + union { + avro_datum_t field; + st_data_t data; + } val; + if (is_avro_datum(datum) && is_avro_record(datum) && field_name) { + if (st_lookup + (avro_datum_to_record(datum)->fields_byname, + (st_data_t) field_name, &(val.data))) { + *field = val.field; + return 0; + } + } + avro_set_error("No field named %s", field_name); + return EINVAL; +} + +int +avro_record_set(avro_datum_t datum, const char *field_name, + const avro_datum_t field_value) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_record(datum), "record datum"); + check_param(EINVAL, field_name, "field_name"); + + char *key = (char *)field_name; + avro_datum_t old_field; + + if (avro_record_get(datum, field_name, &old_field) == 0) { + /* Overriding old value */ + avro_datum_decref(old_field); + } else { + /* Inserting new value */ + struct avro_record_datum_t *record = + avro_datum_to_record(datum); + key = avro_strdup(field_name); + if (!key) { + avro_set_error("Cannot copy field name"); + return ENOMEM; + } + st_insert(record->field_order, + record->field_order->num_entries, + (st_data_t) key); + } + avro_datum_incref(field_value); + st_insert(avro_datum_to_record(datum)->fields_byname, + (st_data_t) key, (st_data_t) field_value); + return 0; +} + +avro_datum_t avro_enum(avro_schema_t schema, int i) +{ + check_param(NULL, is_avro_schema(schema), "schema"); + + struct avro_enum_datum_t *datum = + (struct avro_enum_datum_t *) avro_new(struct avro_enum_datum_t); + if (!datum) { + avro_set_error("Cannot create new enum datum"); + return NULL; + } + datum->schema = avro_schema_incref(schema); + datum->value = i; + + avro_datum_init(&datum->obj, AVRO_ENUM); + return &datum->obj; +} + +int avro_enum_get(const avro_datum_t datum) +{ + return avro_datum_to_enum(datum)->value; +} + +const char *avro_enum_get_name(const avro_datum_t datum) +{ + int value = avro_enum_get(datum); + avro_schema_t schema = avro_datum_to_enum(datum)->schema; + return avro_schema_enum_get(schema, value); +} + +int avro_enum_set(avro_datum_t datum, const int symbol_value) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_enum(datum), "enum datum"); + + avro_datum_to_enum(datum)->value = symbol_value; + return 0; +} + +int avro_enum_set_name(avro_datum_t datum, const char *symbol_name) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_enum(datum), "enum datum"); + check_param(EINVAL, symbol_name, "symbol name"); + + avro_schema_t schema = avro_datum_to_enum(datum)->schema; + int symbol_value = avro_schema_enum_get_by_name(schema, symbol_name); + if (symbol_value == -1) { + avro_set_error("No symbol named %s", symbol_name); + return EINVAL; + } + avro_datum_to_enum(datum)->value = symbol_value; + return 0; +} + +static avro_datum_t avro_fixed_private(avro_schema_t schema, + const char *bytes, const int64_t size, + avro_free_func_t fixed_free) +{ + check_param(NULL, is_avro_schema(schema), "schema"); + struct avro_fixed_schema_t *fschema = avro_schema_to_fixed(schema); + if (size != fschema->size) { + avro_free((char *) bytes, size); + avro_set_error("Fixed size (%zu) doesn't match schema (%zu)", + (size_t) size, (size_t) fschema->size); + return NULL; + } + + struct avro_fixed_datum_t *datum = + (struct avro_fixed_datum_t *) avro_new(struct avro_fixed_datum_t); + if (!datum) { + avro_free((char *) bytes, size); + avro_set_error("Cannot create new fixed datum"); + return NULL; + } + datum->schema = avro_schema_incref(schema); + datum->size = size; + datum->bytes = (char *)bytes; + datum->free = fixed_free; + + avro_datum_init(&datum->obj, AVRO_FIXED); + return &datum->obj; +} + +avro_datum_t avro_fixed(avro_schema_t schema, + const char *bytes, const int64_t size) +{ + char *bytes_copy = (char *) avro_malloc(size); + if (!bytes_copy) { + avro_set_error("Cannot copy fixed content"); + return NULL; + } + memcpy(bytes_copy, bytes, size); + return avro_fixed_private(schema, bytes_copy, size, avro_alloc_free_func); +} + +avro_datum_t avro_givefixed(avro_schema_t schema, + const char *bytes, const int64_t size, + avro_free_func_t free) +{ + return avro_fixed_private(schema, bytes, size, free); +} + +static int avro_fixed_set_private(avro_datum_t datum, + const char *bytes, const int64_t size, + avro_free_func_t fixed_free) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_fixed(datum), "fixed datum"); + + struct avro_fixed_datum_t *fixed = avro_datum_to_fixed(datum); + struct avro_fixed_schema_t *schema = avro_schema_to_fixed(fixed->schema); + if (size != schema->size) { + avro_set_error("Fixed size doesn't match schema"); + return EINVAL; + } + + if (fixed->free) { + fixed->free(fixed->bytes, fixed->size); + } + + fixed->free = fixed_free; + fixed->bytes = (char *)bytes; + fixed->size = size; + return 0; +} + +int avro_fixed_set(avro_datum_t datum, const char *bytes, const int64_t size) +{ + int rval; + char *bytes_copy = (char *) avro_malloc(size); + if (!bytes_copy) { + avro_set_error("Cannot copy fixed content"); + return ENOMEM; + } + memcpy(bytes_copy, bytes, size); + rval = avro_fixed_set_private(datum, bytes_copy, size, avro_alloc_free_func); + if (rval) { + avro_free(bytes_copy, size); + } + return rval; +} + +int avro_givefixed_set(avro_datum_t datum, const char *bytes, + const int64_t size, avro_free_func_t free) +{ + return avro_fixed_set_private(datum, bytes, size, free); +} + +int avro_fixed_get(avro_datum_t datum, char **bytes, int64_t * size) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_fixed(datum), "fixed datum"); + check_param(EINVAL, bytes, "bytes"); + check_param(EINVAL, size, "size"); + + *bytes = avro_datum_to_fixed(datum)->bytes; + *size = avro_datum_to_fixed(datum)->size; + return 0; +} + +static int +avro_init_map(struct avro_map_datum_t *datum) +{ + datum->map = st_init_strtable_with_size(DEFAULT_TABLE_SIZE); + if (!datum->map) { + avro_set_error("Cannot create new map datum"); + return ENOMEM; + } + datum->indices_by_key = st_init_strtable_with_size(DEFAULT_TABLE_SIZE); + if (!datum->indices_by_key) { + avro_set_error("Cannot create new map datum"); + st_free_table(datum->map); + return ENOMEM; + } + datum->keys_by_index = st_init_numtable_with_size(DEFAULT_TABLE_SIZE); + if (!datum->keys_by_index) { + avro_set_error("Cannot create new map datum"); + st_free_table(datum->indices_by_key); + st_free_table(datum->map); + return ENOMEM; + } + return 0; +} + +avro_datum_t avro_map(avro_schema_t schema) +{ + check_param(NULL, is_avro_schema(schema), "schema"); + + struct avro_map_datum_t *datum = + (struct avro_map_datum_t *) avro_new(struct avro_map_datum_t); + if (!datum) { + avro_set_error("Cannot create new map datum"); + return NULL; + } + + if (avro_init_map(datum) != 0) { + avro_freet(struct avro_map_datum_t, datum); + return NULL; + } + + datum->schema = avro_schema_incref(schema); + avro_datum_init(&datum->obj, AVRO_MAP); + return &datum->obj; +} + +size_t +avro_map_size(const avro_datum_t datum) +{ + const struct avro_map_datum_t *map = avro_datum_to_map(datum); + return map->map->num_entries; +} + +int +avro_map_get(const avro_datum_t datum, const char *key, avro_datum_t * value) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_map(datum), "map datum"); + check_param(EINVAL, key, "key"); + check_param(EINVAL, value, "value"); + + union { + avro_datum_t datum; + st_data_t data; + } val; + + struct avro_map_datum_t *map = avro_datum_to_map(datum); + if (st_lookup(map->map, (st_data_t) key, &(val.data))) { + *value = val.datum; + return 0; + } + + avro_set_error("No map element named %s", key); + return EINVAL; +} + +int avro_map_get_key(const avro_datum_t datum, int index, + const char **key) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_map(datum), "map datum"); + check_param(EINVAL, index >= 0, "index"); + check_param(EINVAL, key, "key"); + + union { + st_data_t data; + char *key; + } val; + + struct avro_map_datum_t *map = avro_datum_to_map(datum); + if (st_lookup(map->keys_by_index, (st_data_t) index, &val.data)) { + *key = val.key; + return 0; + } + + avro_set_error("No map element with index %d", index); + return EINVAL; +} + +int avro_map_get_index(const avro_datum_t datum, const char *key, + int *index) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_map(datum), "map datum"); + check_param(EINVAL, key, "key"); + check_param(EINVAL, index, "index"); + + st_data_t data; + + struct avro_map_datum_t *map = avro_datum_to_map(datum); + if (st_lookup(map->indices_by_key, (st_data_t) key, &data)) { + *index = (int) data; + return 0; + } + + avro_set_error("No map element with key %s", key); + return EINVAL; +} + +int +avro_map_set(avro_datum_t datum, const char *key, + const avro_datum_t value) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + check_param(EINVAL, is_avro_map(datum), "map datum"); + check_param(EINVAL, key, "key"); + check_param(EINVAL, is_avro_datum(value), "value"); + + char *save_key = (char *)key; + avro_datum_t old_datum; + + struct avro_map_datum_t *map = avro_datum_to_map(datum); + + if (avro_map_get(datum, key, &old_datum) == 0) { + /* Overwriting an old value */ + avro_datum_decref(old_datum); + } else { + /* Inserting a new value */ + save_key = avro_strdup(key); + if (!save_key) { + avro_set_error("Cannot copy map key"); + return ENOMEM; + } + int new_index = map->map->num_entries; + st_insert(map->indices_by_key, (st_data_t) save_key, + (st_data_t) new_index); + st_insert(map->keys_by_index, (st_data_t) new_index, + (st_data_t) save_key); + } + avro_datum_incref(value); + st_insert(map->map, (st_data_t) save_key, (st_data_t) value); + return 0; +} + +static int +avro_init_array(struct avro_array_datum_t *datum) +{ + datum->els = st_init_numtable_with_size(DEFAULT_TABLE_SIZE); + if (!datum->els) { + avro_set_error("Cannot create new array datum"); + return ENOMEM; + } + return 0; +} + +avro_datum_t avro_array(avro_schema_t schema) +{ + check_param(NULL, is_avro_schema(schema), "schema"); + + struct avro_array_datum_t *datum = + (struct avro_array_datum_t *) avro_new(struct avro_array_datum_t); + if (!datum) { + avro_set_error("Cannot create new array datum"); + return NULL; + } + + if (avro_init_array(datum) != 0) { + avro_freet(struct avro_array_datum_t, datum); + return NULL; + } + + datum->schema = avro_schema_incref(schema); + avro_datum_init(&datum->obj, AVRO_ARRAY); + return &datum->obj; +} + +int +avro_array_get(const avro_datum_t array_datum, int64_t index, avro_datum_t * value) +{ + check_param(EINVAL, is_avro_datum(array_datum), "datum"); + check_param(EINVAL, is_avro_array(array_datum), "array datum"); + check_param(EINVAL, value, "value pointer"); + + union { + st_data_t data; + avro_datum_t datum; + } val; + + const struct avro_array_datum_t * array = avro_datum_to_array(array_datum); + if (st_lookup(array->els, index, &val.data)) { + *value = val.datum; + return 0; + } + + avro_set_error("No array element with index %ld", (long) index); + return EINVAL; +} + +size_t +avro_array_size(const avro_datum_t datum) +{ + const struct avro_array_datum_t *array = avro_datum_to_array(datum); + return array->els->num_entries; +} + +int +avro_array_append_datum(avro_datum_t array_datum, + const avro_datum_t datum) +{ + check_param(EINVAL, is_avro_datum(array_datum), "datum"); + check_param(EINVAL, is_avro_array(array_datum), "array datum"); + check_param(EINVAL, is_avro_datum(datum), "element datum"); + + struct avro_array_datum_t *array = avro_datum_to_array(array_datum); + st_insert(array->els, array->els->num_entries, + (st_data_t) avro_datum_incref(datum)); + return 0; +} + +static int char_datum_free_foreach(char *key, avro_datum_t datum, void *arg) +{ + AVRO_UNUSED(arg); + + avro_datum_decref(datum); + avro_str_free(key); + return ST_DELETE; +} + +static int array_free_foreach(int i, avro_datum_t datum, void *arg) +{ + AVRO_UNUSED(i); + AVRO_UNUSED(arg); + + avro_datum_decref(datum); + return ST_DELETE; +} + +avro_schema_t avro_datum_get_schema(const avro_datum_t datum) +{ + check_param(NULL, is_avro_datum(datum), "datum"); + + switch (avro_typeof(datum)) { + /* + * For the primitive types, which don't store an + * explicit reference to their schema, we decref the + * schema before returning. This maintains the + * invariant that this function doesn't add any + * additional references to the schema. The primitive + * schemas won't be freed, because there's always at + * least 1 reference for their initial static + * initializers. + */ + + case AVRO_STRING: + { + avro_schema_t result = avro_schema_string(); + avro_schema_decref(result); + return result; + } + case AVRO_BYTES: + { + avro_schema_t result = avro_schema_bytes(); + avro_schema_decref(result); + return result; + } + case AVRO_INT32: + { + avro_schema_t result = avro_schema_int(); + avro_schema_decref(result); + return result; + } + case AVRO_INT64: + { + avro_schema_t result = avro_schema_long(); + avro_schema_decref(result); + return result; + } + case AVRO_FLOAT: + { + avro_schema_t result = avro_schema_float(); + avro_schema_decref(result); + return result; + } + case AVRO_DOUBLE: + { + avro_schema_t result = avro_schema_double(); + avro_schema_decref(result); + return result; + } + case AVRO_BOOLEAN: + { + avro_schema_t result = avro_schema_boolean(); + avro_schema_decref(result); + return result; + } + case AVRO_NULL: + { + avro_schema_t result = avro_schema_null(); + avro_schema_decref(result); + return result; + } + + case AVRO_RECORD: + return avro_datum_to_record(datum)->schema; + case AVRO_ENUM: + return avro_datum_to_enum(datum)->schema; + case AVRO_FIXED: + return avro_datum_to_fixed(datum)->schema; + case AVRO_MAP: + return avro_datum_to_map(datum)->schema; + case AVRO_ARRAY: + return avro_datum_to_array(datum)->schema; + case AVRO_UNION: + return avro_datum_to_union(datum)->schema; + + default: + return NULL; + } +} + +static void avro_datum_free(avro_datum_t datum) +{ + if (is_avro_datum(datum)) { + switch (avro_typeof(datum)) { + case AVRO_STRING:{ + struct avro_string_datum_t *string; + string = avro_datum_to_string(datum); + if (string->free) { + string->free(string->s, string->size); + } + avro_freet(struct avro_string_datum_t, string); + } + break; + case AVRO_BYTES:{ + struct avro_bytes_datum_t *bytes; + bytes = avro_datum_to_bytes(datum); + if (bytes->free) { + bytes->free(bytes->bytes, bytes->size); + } + avro_freet(struct avro_bytes_datum_t, bytes); + } + break; + case AVRO_INT32:{ + avro_freet(struct avro_int32_datum_t, datum); + } + break; + case AVRO_INT64:{ + avro_freet(struct avro_int64_datum_t, datum); + } + break; + case AVRO_FLOAT:{ + avro_freet(struct avro_float_datum_t, datum); + } + break; + case AVRO_DOUBLE:{ + avro_freet(struct avro_double_datum_t, datum); + } + break; + case AVRO_BOOLEAN:{ + avro_freet(struct avro_boolean_datum_t, datum); + } + break; + case AVRO_NULL: + /* Nothing allocated */ + break; + + case AVRO_RECORD:{ + struct avro_record_datum_t *record; + record = avro_datum_to_record(datum); + avro_schema_decref(record->schema); + st_foreach(record->fields_byname, + HASH_FUNCTION_CAST char_datum_free_foreach, 0); + st_free_table(record->field_order); + st_free_table(record->fields_byname); + avro_freet(struct avro_record_datum_t, record); + } + break; + case AVRO_ENUM:{ + struct avro_enum_datum_t *enump; + enump = avro_datum_to_enum(datum); + avro_schema_decref(enump->schema); + avro_freet(struct avro_enum_datum_t, enump); + } + break; + case AVRO_FIXED:{ + struct avro_fixed_datum_t *fixed; + fixed = avro_datum_to_fixed(datum); + avro_schema_decref(fixed->schema); + if (fixed->free) { + fixed->free((void *)fixed->bytes, + fixed->size); + } + avro_freet(struct avro_fixed_datum_t, fixed); + } + break; + case AVRO_MAP:{ + struct avro_map_datum_t *map; + map = avro_datum_to_map(datum); + avro_schema_decref(map->schema); + st_foreach(map->map, HASH_FUNCTION_CAST char_datum_free_foreach, + 0); + st_free_table(map->map); + st_free_table(map->indices_by_key); + st_free_table(map->keys_by_index); + avro_freet(struct avro_map_datum_t, map); + } + break; + case AVRO_ARRAY:{ + struct avro_array_datum_t *array; + array = avro_datum_to_array(datum); + avro_schema_decref(array->schema); + st_foreach(array->els, HASH_FUNCTION_CAST array_free_foreach, 0); + st_free_table(array->els); + avro_freet(struct avro_array_datum_t, array); + } + break; + case AVRO_UNION:{ + struct avro_union_datum_t *unionp; + unionp = avro_datum_to_union(datum); + avro_schema_decref(unionp->schema); + avro_datum_decref(unionp->value); + avro_freet(struct avro_union_datum_t, unionp); + } + break; + case AVRO_LINK:{ + /* TODO */ + } + break; + } + } +} + +static int +datum_reset_foreach(int i, avro_datum_t datum, void *arg) +{ + AVRO_UNUSED(i); + int rval; + int *result = (int *) arg; + + rval = avro_datum_reset(datum); + if (rval == 0) { + return ST_CONTINUE; + } else { + *result = rval; + return ST_STOP; + } +} + +int +avro_datum_reset(avro_datum_t datum) +{ + check_param(EINVAL, is_avro_datum(datum), "datum"); + int rval; + + switch (avro_typeof(datum)) { + case AVRO_ARRAY: + { + struct avro_array_datum_t *array; + array = avro_datum_to_array(datum); + st_foreach(array->els, HASH_FUNCTION_CAST array_free_foreach, 0); + st_free_table(array->els); + + rval = avro_init_array(array); + if (rval != 0) { + avro_freet(struct avro_array_datum_t, array); + return rval; + } + return 0; + } + + case AVRO_MAP: + { + struct avro_map_datum_t *map; + map = avro_datum_to_map(datum); + st_foreach(map->map, HASH_FUNCTION_CAST char_datum_free_foreach, 0); + st_free_table(map->map); + st_free_table(map->indices_by_key); + st_free_table(map->keys_by_index); + + rval = avro_init_map(map); + if (rval != 0) { + avro_freet(struct avro_map_datum_t, map); + return rval; + } + return 0; + } + + case AVRO_RECORD: + { + struct avro_record_datum_t *record; + record = avro_datum_to_record(datum); + rval = 0; + st_foreach(record->fields_byname, + HASH_FUNCTION_CAST datum_reset_foreach, (st_data_t) &rval); + return rval; + } + + case AVRO_UNION: + { + struct avro_union_datum_t *unionp; + unionp = avro_datum_to_union(datum); + return (unionp->value == NULL)? 0: + avro_datum_reset(unionp->value); + } + + default: + return 0; + } +} + +avro_datum_t avro_datum_incref(avro_datum_t datum) +{ + if (datum) { + avro_refcount_inc(&datum->refcount); + } + return datum; +} + +void avro_datum_decref(avro_datum_t datum) +{ + if (datum && avro_refcount_dec(&datum->refcount)) { + avro_datum_free(datum); + } +} + +void avro_datum_print(avro_datum_t value, FILE * fp) +{ + AVRO_UNUSED(value); + AVRO_UNUSED(fp); +} diff --git a/fluent-bit/lib/avro/src/datum.h b/fluent-bit/lib/avro/src/datum.h new file mode 100644 index 000000000..c09542b3f --- /dev/null +++ b/fluent-bit/lib/avro/src/datum.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#ifndef AVRO_DATUM_H +#define AVRO_DATUM_H +#include <avro/platform.h> +#include "avro/basics.h" +#include "avro/data.h" +#include "avro/legacy.h" +#include "avro/schema.h" +#include "avro_private.h" +#include "st.h" + +struct avro_string_datum_t { + struct avro_obj_t obj; + char *s; + int64_t size; + avro_free_func_t free; +}; + +struct avro_bytes_datum_t { + struct avro_obj_t obj; + char *bytes; + int64_t size; + avro_free_func_t free; +}; + +struct avro_int32_datum_t { + struct avro_obj_t obj; + int32_t i32; +}; + +struct avro_int64_datum_t { + struct avro_obj_t obj; + int64_t i64; +}; + +struct avro_float_datum_t { + struct avro_obj_t obj; + float f; +}; + +struct avro_double_datum_t { + struct avro_obj_t obj; + double d; +}; + +struct avro_boolean_datum_t { + struct avro_obj_t obj; + int8_t i; +}; + +struct avro_fixed_datum_t { + struct avro_obj_t obj; + avro_schema_t schema; + char *bytes; + int64_t size; + avro_free_func_t free; +}; + +struct avro_map_datum_t { + struct avro_obj_t obj; + avro_schema_t schema; + st_table *map; + st_table *indices_by_key; + st_table *keys_by_index; +}; + +struct avro_record_datum_t { + struct avro_obj_t obj; + avro_schema_t schema; + st_table *field_order; + st_table *fields_byname; +}; + +struct avro_enum_datum_t { + struct avro_obj_t obj; + avro_schema_t schema; + int value; +}; + +struct avro_array_datum_t { + struct avro_obj_t obj; + avro_schema_t schema; + st_table *els; +}; + +struct avro_union_datum_t { + struct avro_obj_t obj; + avro_schema_t schema; + int64_t discriminant; + avro_datum_t value; +}; + +#define avro_datum_to_string(datum_) (container_of(datum_, struct avro_string_datum_t, obj)) +#define avro_datum_to_bytes(datum_) (container_of(datum_, struct avro_bytes_datum_t, obj)) +#define avro_datum_to_int32(datum_) (container_of(datum_, struct avro_int32_datum_t, obj)) +#define avro_datum_to_int64(datum_) (container_of(datum_, struct avro_int64_datum_t, obj)) +#define avro_datum_to_float(datum_) (container_of(datum_, struct avro_float_datum_t, obj)) +#define avro_datum_to_double(datum_) (container_of(datum_, struct avro_double_datum_t, obj)) +#define avro_datum_to_boolean(datum_) (container_of(datum_, struct avro_boolean_datum_t, obj)) +#define avro_datum_to_fixed(datum_) (container_of(datum_, struct avro_fixed_datum_t, obj)) +#define avro_datum_to_map(datum_) (container_of(datum_, struct avro_map_datum_t, obj)) +#define avro_datum_to_record(datum_) (container_of(datum_, struct avro_record_datum_t, obj)) +#define avro_datum_to_enum(datum_) (container_of(datum_, struct avro_enum_datum_t, obj)) +#define avro_datum_to_array(datum_) (container_of(datum_, struct avro_array_datum_t, obj)) +#define avro_datum_to_union(datum_) (container_of(datum_, struct avro_union_datum_t, obj)) + +#endif diff --git a/fluent-bit/lib/avro/src/datum_equal.c b/fluent-bit/lib/avro/src/datum_equal.c new file mode 100644 index 000000000..2ef750f9b --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_equal.c @@ -0,0 +1,186 @@ +/* + * 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_private.h" +#include <string.h> +#include "datum.h" + +static int +array_equal(struct avro_array_datum_t *a, struct avro_array_datum_t *b) +{ + if (!avro_schema_equal(a->schema, b->schema)) { + return 0; + } + + long i; + + if (a->els->num_entries != b->els->num_entries) { + return 0; + } + for (i = 0; i < a->els->num_entries; i++) { + union { + st_data_t data; + avro_datum_t datum; + } ael, bel; + st_lookup(a->els, i, &ael.data); + st_lookup(b->els, i, &bel.data); + if (!avro_datum_equal(ael.datum, bel.datum)) { + return 0; + } + } + return 1; +} + +struct st_equal_args { + int rval; + st_table *st; +}; + +static int +st_equal_foreach(char *key, avro_datum_t datum, struct st_equal_args *args) +{ + union { + avro_datum_t datum_other; + st_data_t data; + } val; + if (!st_lookup(args->st, (st_data_t) key, &(val.data))) { + args->rval = 0; + return ST_STOP; + } + if (!avro_datum_equal(datum, val.datum_other)) { + args->rval = 0; + return ST_STOP; + } + return ST_CONTINUE; +} + +static int map_equal(struct avro_map_datum_t *a, struct avro_map_datum_t *b) +{ + if (!avro_schema_equal(a->schema, b->schema)) { + return 0; + } + + struct st_equal_args args = { 1, b->map }; + if (a->map->num_entries != b->map->num_entries) { + return 0; + } + st_foreach(a->map, HASH_FUNCTION_CAST st_equal_foreach, (st_data_t) & args); + return args.rval; +} + +static int record_equal(struct avro_record_datum_t *a, + struct avro_record_datum_t *b) +{ + if (!avro_schema_equal(a->schema, b->schema)) { + return 0; + } + + struct st_equal_args args = { 1, b->fields_byname }; + if (a->fields_byname->num_entries != b->fields_byname->num_entries) { + return 0; + } + st_foreach(a->fields_byname, HASH_FUNCTION_CAST st_equal_foreach, (st_data_t) & args); + return args.rval; +} + +static int enum_equal(struct avro_enum_datum_t *a, struct avro_enum_datum_t *b) +{ + return avro_schema_equal(a->schema, b->schema) && a->value == b->value; +} + +static int fixed_equal(struct avro_fixed_datum_t *a, + struct avro_fixed_datum_t *b) +{ + if (!avro_schema_equal(a->schema, b->schema)) { + return 0; + } + + return a->size == b->size && memcmp(a->bytes, b->bytes, a->size) == 0; +} + +static int union_equal(struct avro_union_datum_t *a, + struct avro_union_datum_t *b) +{ + if (!avro_schema_equal(a->schema, b->schema)) { + return 0; + } + + return a->discriminant == b->discriminant && avro_datum_equal(a->value, b->value); +} + +int avro_datum_equal(const avro_datum_t a, const avro_datum_t b) +{ + if (!(is_avro_datum(a) && is_avro_datum(b))) { + return 0; + } + if (avro_typeof(a) != avro_typeof(b)) { + return 0; + } + switch (avro_typeof(a)) { + case AVRO_STRING: + return strcmp(avro_datum_to_string(a)->s, + avro_datum_to_string(b)->s) == 0; + case AVRO_BYTES: + return (avro_datum_to_bytes(a)->size == + avro_datum_to_bytes(b)->size) + && memcmp(avro_datum_to_bytes(a)->bytes, + avro_datum_to_bytes(b)->bytes, + avro_datum_to_bytes(a)->size) == 0; + case AVRO_INT32: + return avro_datum_to_int32(a)->i32 == + avro_datum_to_int32(b)->i32; + case AVRO_INT64: + return avro_datum_to_int64(a)->i64 == + avro_datum_to_int64(b)->i64; + case AVRO_FLOAT: + return avro_datum_to_float(a)->f == avro_datum_to_float(b)->f; + case AVRO_DOUBLE: + return avro_datum_to_double(a)->d == avro_datum_to_double(b)->d; + case AVRO_BOOLEAN: + return avro_datum_to_boolean(a)->i == + avro_datum_to_boolean(b)->i; + case AVRO_NULL: + return 1; + case AVRO_ARRAY: + return array_equal(avro_datum_to_array(a), + avro_datum_to_array(b)); + case AVRO_MAP: + return map_equal(avro_datum_to_map(a), avro_datum_to_map(b)); + + case AVRO_RECORD: + return record_equal(avro_datum_to_record(a), + avro_datum_to_record(b)); + + case AVRO_ENUM: + return enum_equal(avro_datum_to_enum(a), avro_datum_to_enum(b)); + + case AVRO_FIXED: + return fixed_equal(avro_datum_to_fixed(a), + avro_datum_to_fixed(b)); + + case AVRO_UNION: + return union_equal(avro_datum_to_union(a), + avro_datum_to_union(b)); + + case AVRO_LINK: + /* + * TODO + */ + return 0; + } + return 0; +} diff --git a/fluent-bit/lib/avro/src/datum_read.c b/fluent-bit/lib/avro/src/datum_read.c new file mode 100644 index 000000000..97bdd54f9 --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_read.c @@ -0,0 +1,99 @@ +/* + * 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 <stdlib.h> +#include <errno.h> + +#include "avro/errors.h" +#include "avro/io.h" +#include "avro/legacy.h" +#include "avro/resolver.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "avro_private.h" + +int +avro_schema_match(avro_schema_t wschema, avro_schema_t rschema) +{ + check_param(0, is_avro_schema(wschema), "writer schema"); + check_param(0, is_avro_schema(rschema), "reader schema"); + + avro_value_iface_t *resolver = + avro_resolved_writer_new(wschema, rschema); + if (resolver != NULL) { + avro_value_iface_decref(resolver); + return 1; + } + + return 0; +} + +int +avro_read_data(avro_reader_t reader, avro_schema_t writers_schema, + avro_schema_t readers_schema, avro_datum_t * datum) +{ + int rval; + + check_param(EINVAL, reader, "reader"); + check_param(EINVAL, is_avro_schema(writers_schema), "writer schema"); + check_param(EINVAL, datum, "datum pointer"); + + if (!readers_schema) { + readers_schema = writers_schema; + } + + avro_datum_t result = avro_datum_from_schema(readers_schema); + if (!result) { + return EINVAL; + } + + avro_value_t value; + check(rval, avro_datum_as_value(&value, result)); + + avro_value_iface_t *resolver = + avro_resolved_writer_new(writers_schema, readers_schema); + if (!resolver) { + avro_value_decref(&value); + avro_datum_decref(result); + return EINVAL; + } + + avro_value_t resolved_value; + rval = avro_resolved_writer_new_value(resolver, &resolved_value); + if (rval) { + avro_value_iface_decref(resolver); + avro_value_decref(&value); + avro_datum_decref(result); + return rval; + } + + avro_resolved_writer_set_dest(&resolved_value, &value); + rval = avro_value_read(reader, &resolved_value); + if (rval) { + avro_value_decref(&resolved_value); + avro_value_iface_decref(resolver); + avro_value_decref(&value); + avro_datum_decref(result); + return rval; + } + + avro_value_decref(&resolved_value); + avro_value_iface_decref(resolver); + avro_value_decref(&value); + *datum = result; + return 0; +} diff --git a/fluent-bit/lib/avro/src/datum_size.c b/fluent-bit/lib/avro/src/datum_size.c new file mode 100644 index 000000000..770cb655f --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_size.c @@ -0,0 +1,292 @@ +/* + * 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_private.h" +#include "avro/errors.h" +#include <errno.h> +#include <assert.h> +#include <string.h> +#include "schema.h" +#include "datum.h" +#include "encoding.h" + +#define size_check(rval, call) { rval = call; if(rval) return rval; } +#define size_accum(rval, size, call) { rval = call; if (rval < 0) return rval; else size += rval; } + +static int64_t size_datum(avro_writer_t writer, const avro_encoding_t * enc, + avro_schema_t writers_schema, avro_datum_t datum); + +static int64_t +size_record(avro_writer_t writer, const avro_encoding_t * enc, + struct avro_record_schema_t *schema, avro_datum_t datum) +{ + int rval; + long i; + int64_t size; + avro_datum_t field_datum; + + size = 0; + if (schema) { + for (i = 0; i < schema->fields->num_entries; i++) { + union { + st_data_t data; + struct avro_record_field_t *field; + } val; + st_lookup(schema->fields, i, &val.data); + size_check(rval, + avro_record_get(datum, val.field->name, + &field_datum)); + size_accum(rval, size, + size_datum(writer, enc, val.field->type, + field_datum)); + } + } else { + /* No schema. Just write the record datum */ + struct avro_record_datum_t *record = + avro_datum_to_record(datum); + for (i = 0; i < record->field_order->num_entries; i++) { + union { + st_data_t data; + char *name; + } val; + st_lookup(record->field_order, i, &val.data); + size_check(rval, + avro_record_get(datum, val.name, + &field_datum)); + size_accum(rval, size, + size_datum(writer, enc, NULL, field_datum)); + } + } + return size; +} + +static int64_t +size_enum(avro_writer_t writer, const avro_encoding_t * enc, + struct avro_enum_schema_t *enump, struct avro_enum_datum_t *datum) +{ + AVRO_UNUSED(enump); + + return enc->size_long(writer, datum->value); +} + +struct size_map_args { + int rval; + int64_t size; + avro_writer_t writer; + const avro_encoding_t *enc; + avro_schema_t values_schema; +}; + +static int +size_map_foreach(char *key, avro_datum_t datum, struct size_map_args *args) +{ + int rval = args->enc->size_string(args->writer, key); + if (rval < 0) { + args->rval = rval; + return ST_STOP; + } else { + args->size += rval; + } + rval = size_datum(args->writer, args->enc, args->values_schema, datum); + if (rval < 0) { + args->rval = rval; + return ST_STOP; + } else { + args->size += rval; + } + return ST_CONTINUE; +} + +static int64_t +size_map(avro_writer_t writer, const avro_encoding_t * enc, + struct avro_map_schema_t *writers_schema, + struct avro_map_datum_t *datum) +{ + int rval; + int64_t size; + struct size_map_args args = { 0, 0, writer, enc, + writers_schema ? writers_schema->values : NULL + }; + + size = 0; + if (datum->map->num_entries) { + size_accum(rval, size, + enc->size_long(writer, datum->map->num_entries)); + st_foreach(datum->map, HASH_FUNCTION_CAST size_map_foreach, (st_data_t) & args); + size += args.size; + } + if (!args.rval) { + size_accum(rval, size, enc->size_long(writer, 0)); + } + return size; +} + +static int64_t +size_array(avro_writer_t writer, const avro_encoding_t * enc, + struct avro_array_schema_t *schema, struct avro_array_datum_t *array) +{ + int rval; + long i; + int64_t size; + + size = 0; + if (array->els->num_entries) { + size_accum(rval, size, + enc->size_long(writer, array->els->num_entries)); + for (i = 0; i < array->els->num_entries; i++) { + union { + st_data_t data; + avro_datum_t datum; + } val; + st_lookup(array->els, i, &val.data); + size_accum(rval, size, + size_datum(writer, enc, + schema ? schema->items : NULL, + val.datum)); + } + } + size_accum(rval, size, enc->size_long(writer, 0)); + return size; +} + +static int64_t +size_union(avro_writer_t writer, const avro_encoding_t * enc, + struct avro_union_schema_t *schema, + struct avro_union_datum_t *unionp) +{ + int rval; + int64_t size; + avro_schema_t write_schema = NULL; + + size = 0; + size_accum(rval, size, enc->size_long(writer, unionp->discriminant)); + if (schema) { + write_schema = + avro_schema_union_branch(&schema->obj, unionp->discriminant); + if (!write_schema) { + return -EINVAL; + } + } + size_accum(rval, size, + size_datum(writer, enc, write_schema, unionp->value)); + return size; +} + +static int64_t size_datum(avro_writer_t writer, const avro_encoding_t * enc, + avro_schema_t writers_schema, avro_datum_t datum) +{ + if (is_avro_schema(writers_schema) && is_avro_link(writers_schema)) { + return size_datum(writer, enc, + (avro_schema_to_link(writers_schema))->to, + datum); + } + + switch (avro_typeof(datum)) { + case AVRO_NULL: + return enc->size_null(writer); + + case AVRO_BOOLEAN: + return enc->size_boolean(writer, + avro_datum_to_boolean(datum)->i); + + case AVRO_STRING: + return enc->size_string(writer, avro_datum_to_string(datum)->s); + + case AVRO_BYTES: + return enc->size_bytes(writer, + avro_datum_to_bytes(datum)->bytes, + avro_datum_to_bytes(datum)->size); + + case AVRO_INT32: + case AVRO_INT64:{ + int64_t val = avro_typeof(datum) == AVRO_INT32 ? + avro_datum_to_int32(datum)->i32 : + avro_datum_to_int64(datum)->i64; + if (is_avro_schema(writers_schema)) { + /* handle promotion */ + if (is_avro_float(writers_schema)) { + return enc->size_float(writer, + (float)val); + } else if (is_avro_double(writers_schema)) { + return enc->size_double(writer, + (double)val); + } + } + return enc->size_long(writer, val); + } + + case AVRO_FLOAT:{ + float val = avro_datum_to_float(datum)->f; + if (is_avro_schema(writers_schema) + && is_avro_double(writers_schema)) { + /* handle promotion */ + return enc->size_double(writer, (double)val); + } + return enc->size_float(writer, val); + } + + case AVRO_DOUBLE: + return enc->size_double(writer, avro_datum_to_double(datum)->d); + + case AVRO_RECORD: + return size_record(writer, enc, + avro_schema_to_record(writers_schema), + datum); + + case AVRO_ENUM: + return size_enum(writer, enc, + avro_schema_to_enum(writers_schema), + avro_datum_to_enum(datum)); + + case AVRO_FIXED: + return avro_datum_to_fixed(datum)->size; + + case AVRO_MAP: + return size_map(writer, enc, + avro_schema_to_map(writers_schema), + avro_datum_to_map(datum)); + + case AVRO_ARRAY: + return size_array(writer, enc, + avro_schema_to_array(writers_schema), + avro_datum_to_array(datum)); + + case AVRO_UNION: + return size_union(writer, enc, + avro_schema_to_union(writers_schema), + avro_datum_to_union(datum)); + + case AVRO_LINK: + break; + } + + return 0; +} + +int64_t avro_size_data(avro_writer_t writer, avro_schema_t writers_schema, + avro_datum_t datum) +{ + check_param(-EINVAL, writer, "writer"); + check_param(-EINVAL, is_avro_datum(datum), "datum"); + /* Only validate datum if a writer's schema is provided */ + if (is_avro_schema(writers_schema) + && !avro_schema_datum_validate(writers_schema, datum)) { + avro_set_error("Datum doesn't validate against schema"); + return -EINVAL; + } + return size_datum(writer, &avro_binary_encoding, writers_schema, datum); +} diff --git a/fluent-bit/lib/avro/src/datum_skip.c b/fluent-bit/lib/avro/src/datum_skip.c new file mode 100644 index 000000000..aa51d7934 --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_skip.c @@ -0,0 +1,202 @@ +/* + * 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_private.h" +#include "avro/errors.h" +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include "encoding.h" +#include "schema.h" + +static int skip_array(avro_reader_t reader, const avro_encoding_t * enc, + struct avro_array_schema_t *writers_schema) +{ + int rval; + int64_t i; + int64_t block_count; + int64_t block_size; + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read array block count: "); + + while (block_count != 0) { + if (block_count < 0) { + block_count = block_count * -1; + check_prefix(rval, enc->read_long(reader, &block_size), + "Cannot read array block size: "); + } + + for (i = 0; i < block_count; i++) { + check_prefix(rval, avro_skip_data(reader, writers_schema->items), + "Cannot skip array element: "); + } + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read array block count: "); + } + return 0; +} + +static int skip_map(avro_reader_t reader, const avro_encoding_t * enc, + struct avro_map_schema_t *writers_schema) +{ + int rval; + int64_t i, block_count; + + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read map block count: "); + while (block_count != 0) { + int64_t block_size; + if (block_count < 0) { + block_count = block_count * -1; + check_prefix(rval, enc->read_long(reader, &block_size), + "Cannot read map block size: "); + } + for (i = 0; i < block_count; i++) { + check_prefix(rval, enc->skip_string(reader), + "Cannot skip map key: "); + check_prefix(rval, + avro_skip_data(reader, + avro_schema_to_map(writers_schema)-> + values), + "Cannot skip map value: "); + } + check_prefix(rval, enc->read_long(reader, &block_count), + "Cannot read map block count: "); + } + return 0; +} + +static int skip_union(avro_reader_t reader, const avro_encoding_t * enc, + struct avro_union_schema_t *writers_schema) +{ + int rval; + int64_t index; + avro_schema_t branch_schema; + + check_prefix(rval, enc->read_long(reader, &index), + "Cannot read union discriminant: "); + branch_schema = avro_schema_union_branch(&writers_schema->obj, index); + if (!branch_schema) { + return EILSEQ; + } + return avro_skip_data(reader, branch_schema); +} + +static int skip_record(avro_reader_t reader, const avro_encoding_t * enc, + struct avro_record_schema_t *writers_schema) +{ + int rval; + long i; + + AVRO_UNUSED(enc); + + for (i = 0; i < writers_schema->fields->num_entries; i++) { + avro_schema_t field_schema; + + field_schema = avro_schema_record_field_get_by_index + (&writers_schema->obj, i); + check_prefix(rval, avro_skip_data(reader, field_schema), + "Cannot skip record field: "); + } + return 0; +} + +int avro_skip_data(avro_reader_t reader, avro_schema_t writers_schema) +{ + check_param(EINVAL, reader, "reader"); + check_param(EINVAL, is_avro_schema(writers_schema), "writer schema"); + + int rval = EINVAL; + const avro_encoding_t *enc = &avro_binary_encoding; + + switch (avro_typeof(writers_schema)) { + case AVRO_NULL: + rval = enc->skip_null(reader); + break; + + case AVRO_BOOLEAN: + rval = enc->skip_boolean(reader); + break; + + case AVRO_STRING: + rval = enc->skip_string(reader); + break; + + case AVRO_INT32: + rval = enc->skip_int(reader); + break; + + case AVRO_INT64: + rval = enc->skip_long(reader); + break; + + case AVRO_FLOAT: + rval = enc->skip_float(reader); + break; + + case AVRO_DOUBLE: + rval = enc->skip_double(reader); + break; + + case AVRO_BYTES: + rval = enc->skip_bytes(reader); + break; + + case AVRO_FIXED: + rval = + avro_skip(reader, + avro_schema_to_fixed(writers_schema)->size); + break; + + case AVRO_ENUM: + rval = enc->skip_long(reader); + break; + + case AVRO_ARRAY: + rval = + skip_array(reader, enc, + avro_schema_to_array(writers_schema)); + break; + + case AVRO_MAP: + rval = + skip_map(reader, enc, avro_schema_to_map(writers_schema)); + break; + + case AVRO_UNION: + rval = + skip_union(reader, enc, + avro_schema_to_union(writers_schema)); + break; + + case AVRO_RECORD: + rval = + skip_record(reader, enc, + avro_schema_to_record(writers_schema)); + break; + + case AVRO_LINK: + rval = + avro_skip_data(reader, + (avro_schema_to_link(writers_schema))->to); + break; + } + + return rval; +} diff --git a/fluent-bit/lib/avro/src/datum_validate.c b/fluent-bit/lib/avro/src/datum_validate.c new file mode 100644 index 000000000..d15ebddda --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_validate.c @@ -0,0 +1,193 @@ +/* + * 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_private.h" +#include "avro/errors.h" +#include <limits.h> +#include <errno.h> +#include <string.h> +#include "schema.h" +#include "datum.h" +#include "st.h" + +struct validate_st { + avro_schema_t expected_schema; + int rval; +}; + +static int +schema_map_validate_foreach(char *key, avro_datum_t datum, + struct validate_st *vst) +{ + AVRO_UNUSED(key); + + if (!avro_schema_datum_validate(vst->expected_schema, datum)) { + vst->rval = 0; + return ST_STOP; + } + return ST_CONTINUE; +} + +int +avro_schema_datum_validate(avro_schema_t expected_schema, avro_datum_t datum) +{ + check_param(EINVAL, expected_schema, "expected schema"); + check_param(EINVAL, is_avro_datum(datum), "datum"); + + int rval; + long i; + + switch (avro_typeof(expected_schema)) { + case AVRO_NULL: + return is_avro_null(datum); + + case AVRO_BOOLEAN: + return is_avro_boolean(datum); + + case AVRO_STRING: + return is_avro_string(datum); + + case AVRO_BYTES: + return is_avro_bytes(datum); + + case AVRO_INT32: + return is_avro_int32(datum) + || (is_avro_int64(datum) + && (INT_MIN <= avro_datum_to_int64(datum)->i64 + && avro_datum_to_int64(datum)->i64 <= INT_MAX)); + + case AVRO_INT64: + return is_avro_int32(datum) || is_avro_int64(datum); + + case AVRO_FLOAT: + return is_avro_int32(datum) || is_avro_int64(datum) + || is_avro_float(datum); + + case AVRO_DOUBLE: + return is_avro_int32(datum) || is_avro_int64(datum) + || is_avro_float(datum) || is_avro_double(datum); + + case AVRO_FIXED: + return (is_avro_fixed(datum) + && (avro_schema_to_fixed(expected_schema)->size == + avro_datum_to_fixed(datum)->size)); + + case AVRO_ENUM: + if (is_avro_enum(datum)) { + long value = avro_datum_to_enum(datum)->value; + long max_value = + avro_schema_to_enum(expected_schema)->symbols-> + num_entries; + return 0 <= value && value <= max_value; + } + return 0; + + case AVRO_ARRAY: + if (is_avro_array(datum)) { + struct avro_array_datum_t *array = + avro_datum_to_array(datum); + + for (i = 0; i < array->els->num_entries; i++) { + union { + st_data_t data; + avro_datum_t datum; + } val; + st_lookup(array->els, i, &val.data); + if (!avro_schema_datum_validate + ((avro_schema_to_array + (expected_schema))->items, val.datum)) { + return 0; + } + } + return 1; + } + return 0; + + case AVRO_MAP: + if (is_avro_map(datum)) { + struct validate_st vst = + { avro_schema_to_map(expected_schema)->values, 1 + }; + st_foreach(avro_datum_to_map(datum)->map, + HASH_FUNCTION_CAST schema_map_validate_foreach, + (st_data_t) & vst); + return vst.rval; + } + break; + + case AVRO_UNION: + if (is_avro_union(datum)) { + struct avro_union_schema_t *union_schema = + avro_schema_to_union(expected_schema); + struct avro_union_datum_t *union_datum = + avro_datum_to_union(datum); + union { + st_data_t data; + avro_schema_t schema; + } val; + + if (!st_lookup + (union_schema->branches, union_datum->discriminant, + &val.data)) { + return 0; + } + return avro_schema_datum_validate(val.schema, + union_datum->value); + } + break; + + case AVRO_RECORD: + if (is_avro_record(datum)) { + struct avro_record_schema_t *record_schema = + avro_schema_to_record(expected_schema); + for (i = 0; i < record_schema->fields->num_entries; i++) { + avro_datum_t field_datum; + union { + st_data_t data; + struct avro_record_field_t *field; + } val; + st_lookup(record_schema->fields, i, &val.data); + + rval = + avro_record_get(datum, val.field->name, + &field_datum); + if (rval) { + /* + * TODO: check for default values + */ + return rval; + } + if (!avro_schema_datum_validate + (val.field->type, field_datum)) { + return 0; + } + } + return 1; + } + break; + + case AVRO_LINK: + { + return + avro_schema_datum_validate((avro_schema_to_link + (expected_schema))->to, + datum); + } + break; + } + return 0; +} diff --git a/fluent-bit/lib/avro/src/datum_value.c b/fluent-bit/lib/avro/src/datum_value.c new file mode 100644 index 000000000..a4fa55a0c --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_value.c @@ -0,0 +1,784 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/basics.h" +#include "avro/errors.h" +#include "avro/legacy.h" +#include "avro/refcount.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "avro_private.h" + +extern avro_value_iface_t AVRO_DATUM_VALUE_CLASS; + +avro_value_iface_t * +avro_datum_class(void) +{ + return &AVRO_DATUM_VALUE_CLASS; +} + +int +avro_datum_as_value(avro_value_t *value, avro_datum_t src) +{ + value->iface = &AVRO_DATUM_VALUE_CLASS; + value->self = avro_datum_incref(src); + return 0; +} + +static int +avro_datum_as_child_value(avro_value_t *value, avro_datum_t src) +{ + value->iface = &AVRO_DATUM_VALUE_CLASS; + value->self = src; + return 0; +} + +static void +avro_datum_value_incref(avro_value_t *value) +{ + avro_datum_t self = (avro_datum_t) value->self; + avro_datum_incref(self); +} + +static void +avro_datum_value_decref(avro_value_t *value) +{ + avro_datum_t self = (avro_datum_t) value->self; + avro_datum_decref(self); +} + +static int +avro_datum_value_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_datum_reset(self); +} + +static avro_type_t +avro_datum_value_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; +#ifdef _WIN32 +#pragma message("#warning: Bug: EINVAL is not of type avro_type_t.") +#else +#warning "Bug: EINVAL is not of type avro_type_t." +#endif + /* We shouldn't use EINVAL as the return value to + * check_param(), because EINVAL (= 22) is not a valid enum + * avro_type_t. This is a structural issue -- we would need a + * different interface on all the get_type functions to fix + * this. For now, suppressing the error by casting EINVAL to + * (avro_type_t) so the code compiles under C++. + */ + check_param((avro_type_t) EINVAL, self, "datum instance"); + return avro_typeof(self); +} + +static avro_schema_t +avro_datum_value_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(NULL, self, "datum instance"); + return avro_datum_get_schema(self); +} + + +static int +avro_datum_value_get_boolean(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + int8_t value; + check(rval, avro_boolean_get(self, &value)); + *out = value; + return 0; +} + +static int +avro_datum_value_get_bytes(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + char *bytes; + int64_t sz; + check(rval, avro_bytes_get(self, &bytes, &sz)); + if (buf != NULL) { + *buf = (const void *) bytes; + } + if (size != NULL) { + *size = sz; + } + return 0; +} + +static int +avro_datum_value_grab_bytes(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + char *bytes; + int64_t sz; + check(rval, avro_bytes_get(self, &bytes, &sz)); + + /* nothing clever, just make a copy */ + return avro_wrapped_buffer_new_copy(dest, bytes, sz); +} + +static int +avro_datum_value_get_double(const avro_value_iface_t *iface, + const void *vself, double *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + double value; + check(rval, avro_double_get(self, &value)); + *out = value; + return 0; +} + +static int +avro_datum_value_get_float(const avro_value_iface_t *iface, + const void *vself, float *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + float value; + check(rval, avro_float_get(self, &value)); + *out = value; + return 0; +} + +static int +avro_datum_value_get_int(const avro_value_iface_t *iface, + const void *vself, int32_t *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + int32_t value; + check(rval, avro_int32_get(self, &value)); + *out = value; + return 0; +} + +static int +avro_datum_value_get_long(const avro_value_iface_t *iface, + const void *vself, int64_t *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + int64_t value; + check(rval, avro_int64_get(self, &value)); + *out = value; + return 0; +} + +static int +avro_datum_value_get_null(const avro_value_iface_t *iface, + const void *vself) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, is_avro_null(self), "datum instance"); + return 0; +} + +static int +avro_datum_value_get_string(const avro_value_iface_t *iface, + const void *vself, const char **str, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + char *value; + check(rval, avro_string_get(self, &value)); + if (str != NULL) { + *str = (const char *) value; + } + if (size != NULL) { + *size = strlen(value)+1; + } + return 0; +} + +static int +avro_datum_value_grab_string(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + char *str; + size_t sz; + check(rval, avro_string_get(self, &str)); + sz = strlen(str); + + /* nothing clever, just make a copy */ + /* sz doesn't contain NUL terminator */ + return avro_wrapped_buffer_new_copy(dest, str, sz+1); +} + +static int +avro_datum_value_get_enum(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, is_avro_enum(self), "datum instance"); + *out = avro_enum_get(self); + return 0; +} + +static int +avro_datum_value_get_fixed(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + char *bytes; + int64_t sz; + check(rval, avro_fixed_get(self, &bytes, &sz)); + if (buf != NULL) { + *buf = (const void *) bytes; + } + if (size != NULL) { + *size = sz; + } + return 0; +} + +static int +avro_datum_value_grab_fixed(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + char *bytes; + int64_t sz; + check(rval, avro_fixed_get(self, &bytes, &sz)); + + /* nothing clever, just make a copy */ + return avro_wrapped_buffer_new_copy(dest, bytes, sz); +} + + +static int +avro_datum_value_set_boolean(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_boolean_set(self, val); +} + +static int +avro_datum_value_set_bytes(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_bytes_set(self, (const char *) buf, size); +} + +static int +avro_datum_value_give_bytes(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + /* + * We actually can't use avro_givebytes_set, since it can't + * handle the extra free_ud parameter. Ah well, this is + * deprecated, so go ahead and make a copy. + */ + + int rval = avro_datum_value_set_bytes + (iface, vself, (void *) buf->buf, buf->size); + avro_wrapped_buffer_free(buf); + return rval; +} + +static int +avro_datum_value_set_double(const avro_value_iface_t *iface, + void *vself, double val) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_double_set(self, val); +} + +static int +avro_datum_value_set_float(const avro_value_iface_t *iface, + void *vself, float val) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_float_set(self, val); +} + +static int +avro_datum_value_set_int(const avro_value_iface_t *iface, + void *vself, int32_t val) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_int32_set(self, val); +} + +static int +avro_datum_value_set_long(const avro_value_iface_t *iface, + void *vself, int64_t val) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_int64_set(self, val); +} + +static int +avro_datum_value_set_null(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, is_avro_null(self), "datum instance"); + return 0; +} + +static int +avro_datum_value_set_string(const avro_value_iface_t *iface, + void *vself, const char *str) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_string_set(self, str); +} + +static int +avro_datum_value_set_string_len(const avro_value_iface_t *iface, + void *vself, const char *str, size_t size) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(size); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_string_set(self, str); +} + +static int +avro_datum_value_give_string_len(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + /* + * We actually can't use avro_givestring_set, since it can't + * handle the extra free_ud parameter. Ah well, this is + * deprecated, so go ahead and make a copy. + */ + + int rval = avro_datum_value_set_string_len + (iface, vself, (char *) buf->buf, buf->size-1); + avro_wrapped_buffer_free(buf); + return rval; +} + +static int +avro_datum_value_set_enum(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_enum_set(self, val); +} + +static int +avro_datum_value_set_fixed(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + return avro_fixed_set(self, (const char *) buf, size); +} + +static int +avro_datum_value_give_fixed(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + /* + * We actually can't use avro_givefixed_set, since it can't + * handle the extra free_ud parameter. Ah well, this is + * deprecated, so go ahead and make a copy. + */ + + int rval = avro_datum_value_set_fixed + (iface, vself, (void *) buf->buf, buf->size); + avro_wrapped_buffer_free(buf); + return rval; +} + + +static int +avro_datum_value_get_size(const avro_value_iface_t *iface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + if (is_avro_array(self)) { + *size = avro_array_size(self); + return 0; + } + + if (is_avro_map(self)) { + *size = avro_map_size(self); + return 0; + } + + if (is_avro_record(self)) { + avro_schema_t schema = avro_datum_get_schema(self); + *size = avro_schema_record_size(schema); + return 0; + } + + avro_set_error("Can only get size of array, map, or record, %d", avro_typeof(self)); + return EINVAL; +} + +static int +avro_datum_value_get_by_index(const avro_value_iface_t *iface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + avro_datum_t child_datum; + + if (is_avro_array(self)) { + check(rval, avro_array_get(self, index, &child_datum)); + return avro_datum_as_child_value(child, child_datum); + } + + if (is_avro_map(self)) { + const char *real_key; + check(rval, avro_map_get_key(self, index, &real_key)); + if (name != NULL) { + *name = real_key; + } + check(rval, avro_map_get(self, real_key, &child_datum)); + return avro_datum_as_child_value(child, child_datum); + } + + if (is_avro_record(self)) { + avro_schema_t schema = avro_datum_get_schema(self); + const char *field_name = + avro_schema_record_field_name(schema, index); + if (field_name == NULL) { + return EINVAL; + } + if (name != NULL) { + *name = field_name; + } + check(rval, avro_record_get(self, field_name, &child_datum)); + return avro_datum_as_child_value(child, child_datum); + } + + avro_set_error("Can only get by index from array, map, or record"); + return EINVAL; +} + +static int +avro_datum_value_get_by_name(const avro_value_iface_t *iface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + int rval; + avro_datum_t child_datum; + + if (is_avro_map(self)) { + if (index != NULL) { + int real_index; + check(rval, avro_map_get_index(self, name, &real_index)); + *index = real_index; + } + + check(rval, avro_map_get(self, name, &child_datum)); + return avro_datum_as_child_value(child, child_datum); + } + + if (is_avro_record(self)) { + if (index != NULL) { + avro_schema_t schema = avro_datum_get_schema(self); + *index = avro_schema_record_field_get_index(schema, name); + } + + check(rval, avro_record_get(self, name, &child_datum)); + return avro_datum_as_child_value(child, child_datum); + } + + avro_set_error("Can only get by name from map or record"); + return EINVAL; +} + +static int +avro_datum_value_get_discriminant(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + if (!is_avro_union(self)) { + avro_set_error("Can only get discriminant of union"); + return EINVAL; + } + + *out = avro_union_discriminant(self); + return 0; +} + +static int +avro_datum_value_get_current_branch(const avro_value_iface_t *iface, + const void *vself, avro_value_t *branch) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + if (!is_avro_union(self)) { + avro_set_error("Can only get current branch of union"); + return EINVAL; + } + + avro_datum_t child_datum = avro_union_current_branch(self); + return avro_datum_as_child_value(branch, child_datum); +} + + +static int +avro_datum_value_append(const avro_value_iface_t *iface, + void *vself, avro_value_t *child_out, size_t *new_index) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + if (!is_avro_array(self)) { + avro_set_error("Can only append to array"); + return EINVAL; + } + + int rval; + + avro_schema_t array_schema = avro_datum_get_schema(self); + avro_schema_t child_schema = avro_schema_array_items(array_schema); + avro_datum_t child_datum = avro_datum_from_schema(child_schema); + if (child_datum == NULL) { + return ENOMEM; + } + + rval = avro_array_append_datum(self, child_datum); + avro_datum_decref(child_datum); + if (rval != 0) { + return rval; + } + + if (new_index != NULL) { + *new_index = avro_array_size(self) - 1; + } + return avro_datum_as_child_value(child_out, child_datum); +} + +static int +avro_datum_value_add(const avro_value_iface_t *iface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + AVRO_UNUSED(iface); + avro_datum_t self = (avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + if (!is_avro_map(self)) { + avro_set_error("Can only add to map"); + return EINVAL; + } + + int rval; + avro_datum_t child_datum; + + if (avro_map_get(self, key, &child_datum) == 0) { + /* key already exists */ + if (is_new != NULL) { + *is_new = 0; + } + if (index != NULL) { + int real_index; + avro_map_get_index(self, key, &real_index); + *index = real_index; + } + return avro_datum_as_child_value(child, child_datum); + } + + /* key is new */ + avro_schema_t map_schema = avro_datum_get_schema(self); + avro_schema_t child_schema = avro_schema_map_values(map_schema); + child_datum = avro_datum_from_schema(child_schema); + if (child_datum == NULL) { + return ENOMEM; + } + + rval = avro_map_set(self, key, child_datum); + avro_datum_decref(child_datum); + if (rval != 0) { + return rval; + } + + if (is_new != NULL) { + *is_new = 1; + } + if (index != NULL) { + *index = avro_map_size(self) - 1; + } + + return avro_datum_as_child_value(child, child_datum); +} + +static int +avro_datum_value_set_branch(const avro_value_iface_t *iface, + void *vself, int discriminant, + avro_value_t *branch) +{ + AVRO_UNUSED(iface); + const avro_datum_t self = (const avro_datum_t) vself; + check_param(EINVAL, self, "datum instance"); + + if (!is_avro_union(self)) { + avro_set_error("Can only set branch of union"); + return EINVAL; + } + + int rval; + avro_datum_t child_datum; + check(rval, avro_union_set_discriminant(self, discriminant, &child_datum)); + return avro_datum_as_child_value(branch, child_datum); +} + + +avro_value_iface_t AVRO_DATUM_VALUE_CLASS = +{ + /* "class" methods */ + NULL, /* incref */ + NULL, /* decref */ + /* general "instance" methods */ + avro_datum_value_incref, + avro_datum_value_decref, + avro_datum_value_reset, + avro_datum_value_get_type, + avro_datum_value_get_schema, + /* primitive getters */ + avro_datum_value_get_boolean, + avro_datum_value_get_bytes, + avro_datum_value_grab_bytes, + avro_datum_value_get_double, + avro_datum_value_get_float, + avro_datum_value_get_int, + avro_datum_value_get_long, + avro_datum_value_get_null, + avro_datum_value_get_string, + avro_datum_value_grab_string, + avro_datum_value_get_enum, + avro_datum_value_get_fixed, + avro_datum_value_grab_fixed, + /* primitive setters */ + avro_datum_value_set_boolean, + avro_datum_value_set_bytes, + avro_datum_value_give_bytes, + avro_datum_value_set_double, + avro_datum_value_set_float, + avro_datum_value_set_int, + avro_datum_value_set_long, + avro_datum_value_set_null, + avro_datum_value_set_string, + avro_datum_value_set_string_len, + avro_datum_value_give_string_len, + avro_datum_value_set_enum, + avro_datum_value_set_fixed, + avro_datum_value_give_fixed, + /* compound getters */ + avro_datum_value_get_size, + avro_datum_value_get_by_index, + avro_datum_value_get_by_name, + avro_datum_value_get_discriminant, + avro_datum_value_get_current_branch, + /* compound setters */ + avro_datum_value_append, + avro_datum_value_add, + avro_datum_value_set_branch +}; diff --git a/fluent-bit/lib/avro/src/datum_write.c b/fluent-bit/lib/avro/src/datum_write.c new file mode 100644 index 000000000..f3714ab84 --- /dev/null +++ b/fluent-bit/lib/avro/src/datum_write.c @@ -0,0 +1,91 @@ +/* + * 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 <assert.h> +#include <errno.h> +#include <string.h> + +#include "avro/basics.h" +#include "avro/errors.h" +#include "avro/io.h" +#include "avro/legacy.h" +#include "avro/resolver.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "avro_private.h" + +int avro_write_data(avro_writer_t writer, avro_schema_t writers_schema, + avro_datum_t datum) +{ + int rval; + + check_param(EINVAL, writer, "writer"); + check_param(EINVAL, is_avro_datum(datum), "datum"); + + /* Only validate datum if a writer's schema is provided */ + if (is_avro_schema(writers_schema)) { + if (!avro_schema_datum_validate(writers_schema, datum)) { + avro_set_error("Datum doesn't validate against schema"); + return EINVAL; + } + + /* + * Some confusing terminology here. The "writers_schema" + * parameter is the schema we want to use to write the data + * into the "writer" buffer. Before doing that, we need to + * resolve the datum from its actual schema into this + * "writer" schema. For the purposes of that resolution, + * the writer schema is the datum's actual schema, and the + * reader schema is our eventual (when writing to the + * buffer) "writer" schema. + */ + + avro_schema_t datum_schema = avro_datum_get_schema(datum); + avro_value_iface_t *resolver = + avro_resolved_reader_new(datum_schema, writers_schema); + if (resolver == NULL) { + return EINVAL; + } + + avro_value_t value; + check(rval, avro_datum_as_value(&value, datum)); + + avro_value_t resolved; + rval = avro_resolved_reader_new_value(resolver, &resolved); + if (rval != 0) { + avro_value_decref(&value); + avro_value_iface_decref(resolver); + return rval; + } + + avro_resolved_reader_set_source(&resolved, &value); + rval = avro_value_write(writer, &resolved); + avro_value_decref(&resolved); + avro_value_decref(&value); + avro_value_iface_decref(resolver); + return rval; + } + + /* If we're writing using the datum's actual schema, we don't + * need a resolver. */ + + avro_value_t value; + check(rval, avro_datum_as_value(&value, datum)); + check(rval, avro_value_write(writer, &value)); + avro_value_decref(&value); + return 0; +} diff --git a/fluent-bit/lib/avro/src/dump.c b/fluent-bit/lib/avro/src/dump.c new file mode 100644 index 000000000..5b8f1c99b --- /dev/null +++ b/fluent-bit/lib/avro/src/dump.c @@ -0,0 +1,56 @@ +/* + * 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 <ctype.h> +#include <string.h> +#include "avro_private.h" +#include "dump.h" + +static void dump_line(FILE * out, const char *addr, const long len) +{ + int i; + fprintf(out, "|"); + for (i = 0; i < 16; i++) { + if (i < len) { + fprintf(out, " %02X", ((uint8_t *) addr)[i]); + } else { + fprintf(out, " .."); + } + if (!((i + 1) % 8)) { + fprintf(out, " |"); + } + } + fprintf(out, "\t"); + for (i = 0; i < 16; i++) { + char c = 0x7f & ((uint8_t *) addr)[i]; + if (i < len && isprint(c)) { + fprintf(out, "%c", c); + } else { + fprintf(out, "."); + } + } +} + +void dump(FILE * out, const char *addr, const long len) +{ + int i; + for (i = 0; i < len; i += 16) { + dump_line(out, addr + i, (len - i) < 16 ? (len - i) : 16); + fprintf(out, "\n"); + } + fflush(out); +} diff --git a/fluent-bit/lib/avro/src/dump.h b/fluent-bit/lib/avro/src/dump.h new file mode 100644 index 000000000..23e806672 --- /dev/null +++ b/fluent-bit/lib/avro/src/dump.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef DUMP_H +#define DUMP_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <stdio.h> + +#pragma GCC visibility push(hidden) +void dump(FILE * out, const char *addr, const long len); +#pragma GCC visibility pop + +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/encoding.h b/fluent-bit/lib/avro/src/encoding.h new file mode 100644 index 000000000..6333d588d --- /dev/null +++ b/fluent-bit/lib/avro/src/encoding.h @@ -0,0 +1,106 @@ +/* + * 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. + */ +#ifndef AVRO_ENCODING_H +#define AVRO_ENCODING_H +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> +#include "avro/io.h" + +/* + * TODO: this will need more functions when JSON encoding is added + */ +struct avro_encoding_t { + const char *description; + /* + * string + */ + int (*read_string) (avro_reader_t reader, char **s, int64_t *len); + int (*skip_string) (avro_reader_t reader); + int (*write_string) (avro_writer_t writer, const char *s); + int64_t(*size_string) (avro_writer_t writer, const char *s); + /* + * bytes + */ + int (*read_bytes) (avro_reader_t reader, char **bytes, int64_t * len); + int (*skip_bytes) (avro_reader_t reader); + int (*write_bytes) (avro_writer_t writer, + const char *bytes, const int64_t len); + int64_t(*size_bytes) (avro_writer_t writer, + const char *bytes, const int64_t len); + /* + * int + */ + int (*read_int) (avro_reader_t reader, int32_t * i); + int (*skip_int) (avro_reader_t reader); + int (*write_int) (avro_writer_t writer, const int32_t i); + int64_t(*size_int) (avro_writer_t writer, const int32_t i); + /* + * long + */ + int (*read_long) (avro_reader_t reader, int64_t * l); + int (*skip_long) (avro_reader_t reader); + int (*write_long) (avro_writer_t writer, const int64_t l); + int64_t(*size_long) (avro_writer_t writer, const int64_t l); + /* + * float + */ + int (*read_float) (avro_reader_t reader, float *f); + int (*skip_float) (avro_reader_t reader); + int (*write_float) (avro_writer_t writer, const float f); + int64_t(*size_float) (avro_writer_t writer, const float f); + /* + * double + */ + int (*read_double) (avro_reader_t reader, double *d); + int (*skip_double) (avro_reader_t reader); + int (*write_double) (avro_writer_t writer, const double d); + int64_t(*size_double) (avro_writer_t writer, const double d); + /* + * boolean + */ + int (*read_boolean) (avro_reader_t reader, int8_t * b); + int (*skip_boolean) (avro_reader_t reader); + int (*write_boolean) (avro_writer_t writer, const int8_t b); + int64_t(*size_boolean) (avro_writer_t writer, const int8_t b); + /* + * null + */ + int (*read_null) (avro_reader_t reader); + int (*skip_null) (avro_reader_t reader); + int (*write_null) (avro_writer_t writer); + int64_t(*size_null) (avro_writer_t writer); +}; +typedef struct avro_encoding_t avro_encoding_t; + +#define AVRO_WRITE(writer, buf, len) \ +{ int rval = avro_write( writer, buf, len ); if(rval) return rval; } +#define AVRO_READ(reader, buf, len) \ +{ int rval = avro_read( reader, buf, len ); if(rval) return rval; } +#define AVRO_SKIP(reader, len) \ +{ int rval = avro_skip( reader, len); if (rval) return rval; } + +extern const avro_encoding_t avro_binary_encoding; /* in + * encoding_binary + */ +CLOSE_EXTERN +#endif diff --git a/fluent-bit/lib/avro/src/encoding_binary.c b/fluent-bit/lib/avro/src/encoding_binary.c new file mode 100644 index 000000000..1fc5f0c9a --- /dev/null +++ b/fluent-bit/lib/avro/src/encoding_binary.c @@ -0,0 +1,446 @@ +/* + * 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_private.h" +#include "avro/allocation.h" +#include "avro/errors.h" +#include "encoding.h" +#include <stdlib.h> +#include <limits.h> +#include <errno.h> +#include <string.h> + +#define MAX_VARINT_BUF_SIZE 10 + +static int read_long(avro_reader_t reader, int64_t * l) +{ + uint64_t value = 0; + uint8_t b; + int offset = 0; + do { + if (offset == MAX_VARINT_BUF_SIZE) { + /* + * illegal byte sequence + */ + avro_set_error("Varint too long"); + return EILSEQ; + } + AVRO_READ(reader, &b, 1); + value |= (int64_t) (b & 0x7F) << (7 * offset); + ++offset; + } + while (b & 0x80); + *l = ((value >> 1) ^ -(value & 1)); + return 0; +} + +static int skip_long(avro_reader_t reader) +{ + uint8_t b; + int offset = 0; + do { + if (offset == MAX_VARINT_BUF_SIZE) { + avro_set_error("Varint too long"); + return EILSEQ; + } + AVRO_READ(reader, &b, 1); + ++offset; + } + while (b & 0x80); + return 0; +} + +static int write_long(avro_writer_t writer, int64_t l) +{ + char buf[MAX_VARINT_BUF_SIZE]; + uint8_t bytes_written = 0; + uint64_t n = (l << 1) ^ (l >> 63); + while (n & ~0x7F) { + buf[bytes_written++] = (char)((((uint8_t) n) & 0x7F) | 0x80); + n >>= 7; + } + buf[bytes_written++] = (char)n; + AVRO_WRITE(writer, buf, bytes_written); + return 0; +} + +static int64_t size_long(avro_writer_t writer, int64_t l) +{ + AVRO_UNUSED(writer); + + int64_t len = 0; + uint64_t n = (l << 1) ^ (l >> 63); + while (n & ~0x7F) { + len++; + n >>= 7; + } + len++; + return len; +} + +static int read_int(avro_reader_t reader, int32_t * i) +{ + int64_t l; + int rval; + check(rval, read_long(reader, &l)); + if (!(INT_MIN <= l && l <= INT_MAX)) { + avro_set_error("Varint out of range for int type"); + return ERANGE; + } + *i = l; + return 0; +} + +static int skip_int(avro_reader_t reader) +{ + return skip_long(reader); +} + +static int write_int(avro_writer_t writer, const int32_t i) +{ + int64_t l = i; + return write_long(writer, l); +} + +static int64_t size_int(avro_writer_t writer, const int32_t i) +{ + int64_t l = i; + return size_long(writer, l); +} + +static int read_bytes(avro_reader_t reader, char **bytes, int64_t * len) +{ + int rval; + check_prefix(rval, read_long(reader, len), + "Cannot read bytes length: "); + *bytes = (char *) avro_malloc(*len + 1); + if (!*bytes) { + avro_set_error("Cannot allocate buffer for bytes value"); + return ENOMEM; + } + AVRO_READ(reader, *bytes, *len); + (*bytes)[*len] = '\0'; + return 0; +} + +static int skip_bytes(avro_reader_t reader) +{ + int64_t len = 0; + int rval; + check_prefix(rval, read_long(reader, &len), + "Cannot read bytes length: "); + AVRO_SKIP(reader, len); + return 0; +} + +static int +write_bytes(avro_writer_t writer, const char *bytes, const int64_t len) +{ + int rval; + if (len < 0) { + avro_set_error("Invalid bytes value length"); + return EINVAL; + } + check_prefix(rval, write_long(writer, len), + "Cannot write bytes length: "); + AVRO_WRITE(writer, (char *)bytes, len); + return 0; +} + +static int64_t +size_bytes(avro_writer_t writer, const char *bytes, const int64_t len) +{ + AVRO_UNUSED(bytes); + + return size_long(writer, len) + len; +} + +static int read_string(avro_reader_t reader, char **s, int64_t *len) +{ + int64_t str_len = 0; + int rval; + check_prefix(rval, read_long(reader, &str_len), + "Cannot read string length: "); + *len = str_len + 1; + *s = (char *) avro_malloc(*len); + if (!*s) { + avro_set_error("Cannot allocate buffer for string value"); + return ENOMEM; + } + (*s)[str_len] = '\0'; + AVRO_READ(reader, *s, str_len); + return 0; +} + +static int skip_string(avro_reader_t reader) +{ + return skip_bytes(reader); +} + +static int write_string(avro_writer_t writer, const char *s) +{ + int64_t len = strlen(s); + return write_bytes(writer, s, len); +} + +static int64_t size_string(avro_writer_t writer, const char *s) +{ + int64_t len = strlen(s); + return size_bytes(writer, s, len); +} + +static int read_float(avro_reader_t reader, float *f) +{ +#if AVRO_PLATFORM_IS_BIG_ENDIAN + uint8_t buf[4]; +#endif + union { + float f; + int32_t i; + } v; +#if AVRO_PLATFORM_IS_BIG_ENDIAN + AVRO_READ(reader, buf, 4); + v.i = ((int32_t) buf[0] << 0) + | ((int32_t) buf[1] << 8) + | ((int32_t) buf[2] << 16) | ((int32_t) buf[3] << 24); +#else + AVRO_READ(reader, (void *)&v.i, 4); +#endif + *f = v.f; + return 0; +} + +static int skip_float(avro_reader_t reader) +{ + AVRO_SKIP(reader, 4); + return 0; +} + +static int write_float(avro_writer_t writer, const float f) +{ +#if AVRO_PLATFORM_IS_BIG_ENDIAN + uint8_t buf[4]; +#endif + union { + float f; + int32_t i; + } v; + + v.f = f; +#if AVRO_PLATFORM_IS_BIG_ENDIAN + buf[0] = (uint8_t) (v.i >> 0); + buf[1] = (uint8_t) (v.i >> 8); + buf[2] = (uint8_t) (v.i >> 16); + buf[3] = (uint8_t) (v.i >> 24); + AVRO_WRITE(writer, buf, 4); +#else + AVRO_WRITE(writer, (void *)&v.i, 4); +#endif + return 0; +} + +static int64_t size_float(avro_writer_t writer, const float f) +{ + AVRO_UNUSED(writer); + AVRO_UNUSED(f); + + return 4; +} + +static int read_double(avro_reader_t reader, double *d) +{ +#if AVRO_PLATFORM_IS_BIG_ENDIAN + uint8_t buf[8]; +#endif + union { + double d; + int64_t l; + } v; + +#if AVRO_PLATFORM_IS_BIG_ENDIAN + AVRO_READ(reader, buf, 8); + v.l = ((int64_t) buf[0] << 0) + | ((int64_t) buf[1] << 8) + | ((int64_t) buf[2] << 16) + | ((int64_t) buf[3] << 24) + | ((int64_t) buf[4] << 32) + | ((int64_t) buf[5] << 40) + | ((int64_t) buf[6] << 48) | ((int64_t) buf[7] << 56); +#else + AVRO_READ(reader, (void *)&v.l, 8); +#endif + *d = v.d; + return 0; +} + +static int skip_double(avro_reader_t reader) +{ + AVRO_SKIP(reader, 8); + return 0; +} + +static int write_double(avro_writer_t writer, const double d) +{ +#if AVRO_PLATFORM_IS_BIG_ENDIAN + uint8_t buf[8]; +#endif + union { + double d; + int64_t l; + } v; + + v.d = d; +#if AVRO_PLATFORM_IS_BIG_ENDIAN + buf[0] = (uint8_t) (v.l >> 0); + buf[1] = (uint8_t) (v.l >> 8); + buf[2] = (uint8_t) (v.l >> 16); + buf[3] = (uint8_t) (v.l >> 24); + buf[4] = (uint8_t) (v.l >> 32); + buf[5] = (uint8_t) (v.l >> 40); + buf[6] = (uint8_t) (v.l >> 48); + buf[7] = (uint8_t) (v.l >> 56); + AVRO_WRITE(writer, buf, 8); +#else + AVRO_WRITE(writer, (void *)&v.l, 8); +#endif + return 0; +} + +static int64_t size_double(avro_writer_t writer, const double d) +{ + AVRO_UNUSED(writer); + AVRO_UNUSED(d); + + return 8; +} + +static int read_boolean(avro_reader_t reader, int8_t * b) +{ + AVRO_READ(reader, b, 1); + return 0; +} + +static int skip_boolean(avro_reader_t reader) +{ + AVRO_SKIP(reader, 1); + return 0; +} + +static int write_boolean(avro_writer_t writer, const int8_t b) +{ + AVRO_WRITE(writer, (char *)&b, 1); + return 0; +} + +static int64_t size_boolean(avro_writer_t writer, const int8_t b) +{ + AVRO_UNUSED(writer); + AVRO_UNUSED(b); + + return 1; +} + +static int read_skip_null(avro_reader_t reader) +{ + /* + * no-op + */ + AVRO_UNUSED(reader); + + return 0; +} + +static int write_null(avro_writer_t writer) +{ + /* + * no-op + */ + AVRO_UNUSED(writer); + + return 0; +} + +static int64_t size_null(avro_writer_t writer) +{ + AVRO_UNUSED(writer); + + return 0; +} + +/* Win32 doesn't support the C99 method of initializing named elements + * in a struct declaration. So hide the named parameters for Win32, + * and initialize in the order the code was written. + */ +const avro_encoding_t avro_binary_encoding = { + /* .description = */ "BINARY FORMAT", + /* + * string + */ + /* .read_string = */ read_string, + /* .skip_string = */ skip_string, + /* .write_string = */ write_string, + /* .size_string = */ size_string, + /* + * bytes + */ + /* .read_bytes = */ read_bytes, + /* .skip_bytes = */ skip_bytes, + /* .write_bytes = */ write_bytes, + /* .size_bytes = */ size_bytes, + /* + * int + */ + /* .read_int = */ read_int, + /* .skip_int = */ skip_int, + /* .write_int = */ write_int, + /* .size_int = */ size_int, + /* + * long + */ + /* .read_long = */ read_long, + /* .skip_long = */ skip_long, + /* .write_long = */ write_long, + /* .size_long = */ size_long, + /* + * float + */ + /* .read_float = */ read_float, + /* .skip_float = */ skip_float, + /* .write_float = */ write_float, + /* .size_float = */ size_float, + /* + * double + */ + /* .read_double = */ read_double, + /* .skip_double = */ skip_double, + /* .write_double = */ write_double, + /* .size_double = */ size_double, + /* + * boolean + */ + /* .read_boolean = */ read_boolean, + /* .skip_boolean = */ skip_boolean, + /* .write_boolean = */ write_boolean, + /* .size_boolean = */ size_boolean, + /* + * null + */ + /* .read_null = */ read_skip_null, + /* .skip_null = */ read_skip_null, + /* .write_null = */ write_null, + /* .size_null = */ size_null +}; diff --git a/fluent-bit/lib/avro/src/errors.c b/fluent-bit/lib/avro/src/errors.c new file mode 100644 index 000000000..8abd8c832 --- /dev/null +++ b/fluent-bit/lib/avro/src/errors.c @@ -0,0 +1,160 @@ +/* + * 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 <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "avro/errors.h" + +/* 4K should be enough, right? */ +#define AVRO_ERROR_SIZE 4096 + +/* + * To support the avro_prefix_error function, we keep two string buffers + * around. The AVRO_CURRENT_ERROR points at the buffer that's holding + * the current error message. avro_prefix error writes into the other + * buffer, and then swaps them. + */ + +struct avro_error_data_t { + char AVRO_ERROR1[AVRO_ERROR_SIZE]; + char AVRO_ERROR2[AVRO_ERROR_SIZE]; + + char *AVRO_CURRENT_ERROR; + char *AVRO_OTHER_ERROR; +}; + + +#if defined THREADSAFE +#if ( defined __unix__ || defined __unix ) +#include <pthread.h> +static pthread_key_t error_data_key; +static pthread_once_t error_data_key_once = PTHREAD_ONCE_INIT; + +static void make_error_data_key() +{ + pthread_key_create(&error_data_key, free); +} +#elif defined _WIN32 +#include <Windows.h> + +static __declspec( thread ) struct avro_error_data_t TLS_ERROR_DATA = { "", "", NULL, NULL }; + +#endif /* unix||_unix||_WIN32 */ +#endif /* THREADSAFE */ + +static struct avro_error_data_t * +avro_get_error_data(void) +{ +#if defined THREADSAFE +#if defined __unix__ || defined __unix + + pthread_once(&error_data_key_once, make_error_data_key); + + struct avro_error_data_t *ERROR_DATA = + (struct avro_error_data_t*) pthread_getspecific(error_data_key); + + if (!ERROR_DATA) { + ERROR_DATA = (struct avro_error_data_t*) malloc(sizeof(struct avro_error_data_t)); + pthread_setspecific(error_data_key, ERROR_DATA); + + ERROR_DATA->AVRO_ERROR1[0] = '\0'; + ERROR_DATA->AVRO_ERROR2[0] = '\0'; + ERROR_DATA->AVRO_CURRENT_ERROR = ERROR_DATA->AVRO_ERROR1; + ERROR_DATA->AVRO_OTHER_ERROR = ERROR_DATA->AVRO_ERROR2; + } + + return ERROR_DATA; + +#elif defined _WIN32 + + if ( TLS_ERROR_DATA.AVRO_CURRENT_ERROR == NULL ) + { + //first usage of the ERROR_DATA, initialize 'current' and 'other' pointers. + TLS_ERROR_DATA.AVRO_CURRENT_ERROR = TLS_ERROR_DATA.AVRO_ERROR1; + TLS_ERROR_DATA.AVRO_OTHER_ERROR = TLS_ERROR_DATA.AVRO_ERROR2; + } + return &TLS_ERROR_DATA; + + #endif /* UNIX and WIN32 threadsafe handling */ + +#else /* not thread-safe */ + static struct avro_error_data_t ERROR_DATA = { + /* .AVRO_ERROR1 = */ {'\0'}, + /* .AVRO_ERROR2 = */ {'\0'}, + /* .AVRO_CURRENT_ERROR = */ ERROR_DATA.AVRO_ERROR1, + /* .AVRO_OTHER_ERROR = */ ERROR_DATA.AVRO_ERROR2, + }; + + return &ERROR_DATA; +#endif +} + + +void +avro_set_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(avro_get_error_data()->AVRO_CURRENT_ERROR, AVRO_ERROR_SIZE, fmt, args); + va_end(args); + //fprintf(stderr, "--- %s\n", AVRO_CURRENT_ERROR); +} + + +void +avro_prefix_error(const char *fmt, ...) +{ + struct avro_error_data_t *ERROR_DATA = avro_get_error_data(); + + /* + * First render the prefix into OTHER_ERROR. + */ + + va_list args; + va_start(args, fmt); + int bytes_written = vsnprintf(ERROR_DATA->AVRO_OTHER_ERROR, AVRO_ERROR_SIZE, fmt, args); + va_end(args); + + /* + * Then concatenate the existing error onto the end. + */ + + if (bytes_written < AVRO_ERROR_SIZE) { + strncpy(&ERROR_DATA->AVRO_OTHER_ERROR[bytes_written], ERROR_DATA->AVRO_CURRENT_ERROR, + AVRO_ERROR_SIZE - bytes_written); + ERROR_DATA->AVRO_OTHER_ERROR[AVRO_ERROR_SIZE-1] = '\0'; + } + + /* + * Swap the two error pointers. + */ + + char *tmp; + tmp = ERROR_DATA->AVRO_OTHER_ERROR; + ERROR_DATA->AVRO_OTHER_ERROR = ERROR_DATA->AVRO_CURRENT_ERROR; + ERROR_DATA->AVRO_CURRENT_ERROR = tmp; + //fprintf(stderr, "+++ %s\n", AVRO_CURRENT_ERROR); +} + + +const char *avro_strerror(void) +{ + return avro_get_error_data()->AVRO_CURRENT_ERROR; +} diff --git a/fluent-bit/lib/avro/src/generic.c b/fluent-bit/lib/avro/src/generic.c new file mode 100644 index 000000000..e614eb3f8 --- /dev/null +++ b/fluent-bit/lib/avro/src/generic.c @@ -0,0 +1,3707 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro/generic.h" +#include "avro/refcount.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "avro_generic_internal.h" +#include "avro_private.h" + + +/*----------------------------------------------------------------------- + * Forward definitions + */ + +typedef struct avro_generic_link_value_iface avro_generic_link_value_iface_t; + +typedef struct memoize_state_t { + avro_memoize_t mem; + avro_generic_link_value_iface_t *links; +} memoize_state_t; + +static avro_generic_value_iface_t * +avro_generic_class_from_schema_memoized(avro_schema_t schema, + memoize_state_t *state); + + +/*----------------------------------------------------------------------- + * Generic support functions + */ + +int +avro_generic_value_new(avro_value_iface_t *iface, avro_value_t *dest) +{ + int rval; + avro_generic_value_iface_t *giface = + container_of(iface, avro_generic_value_iface_t, parent); + size_t instance_size = avro_value_instance_size(giface); + void *self = avro_malloc(instance_size + sizeof(volatile int)); + if (self == NULL) { + avro_set_error(strerror(ENOMEM)); + dest->iface = NULL; + dest->self = NULL; + return ENOMEM; + } + + volatile int *refcount = (volatile int *) self; + self = (char *) self + sizeof(volatile int); + + *refcount = 1; + rval = avro_value_init(giface, self); + if (rval != 0) { + avro_free(self, instance_size); + dest->iface = NULL; + dest->self = NULL; + return rval; + } + + dest->iface = avro_value_iface_incref(&giface->parent); + dest->self = self; + return 0; +} + +static void +avro_generic_value_free(const avro_value_iface_t *iface, void *self) +{ + if (self != NULL) { + const avro_generic_value_iface_t *giface = + container_of(iface, avro_generic_value_iface_t, parent); + size_t instance_size = avro_value_instance_size(giface); + avro_value_done(giface, self); + self = (char *) self - sizeof(volatile int); + avro_free(self, instance_size + sizeof(volatile int)); + } +} + +static void +avro_generic_value_incref(avro_value_t *value) +{ + /* + * This only works if you pass in the top-level value. + */ + + volatile int *refcount = (volatile int *) ((char *) value->self - sizeof(volatile int)); + avro_refcount_inc(refcount); +} + +static void +avro_generic_value_decref(avro_value_t *value) +{ + /* + * This only works if you pass in the top-level value. + */ + + volatile int *refcount = (volatile int *) ((char *) value->self - sizeof(volatile int)); + if (avro_refcount_dec(refcount)) { + avro_generic_value_free(value->iface, value->self); + } +} + + +/*----------------------------------------------------------------------- + * Recursive schemas + */ + +/* + * Recursive schemas are handled specially; the value implementation for + * an AVRO_LINK schema is simply a wrapper around the value + * implementation for the link's target schema. The value methods all + * delegate to the wrapped implementation. + * + * We don't set the target_iface pointer when the link implementation is + * first created, since we might not have finished creating the + * implementation for the target schema. (We create the implementations + * for child schemas depth-first, so the target schema's implementation + * won't be done until all of its descendants — including the link + * schema — have been instantiated.) + * + * So anyway, we set the target_iface pointer to NULL at first. And + * then in a fix-up stage, once all of the non-link schemas have been + * instantiated, we go through and set the target_iface pointers for any + * link schemas we encountered. + */ + +struct avro_generic_link_value_iface { + avro_generic_value_iface_t parent; + + /** The reference count for this interface. */ + volatile int refcount; + + /** The schema for this interface. */ + avro_schema_t schema; + + /** The target's implementation. */ + avro_generic_value_iface_t *target_giface; + + /** + * A pointer to the “next” link interface that we've had to + * create. We use this as we're creating the overall top-level + * value interface to keep track of which ones we have to fix up + * afterwards. + */ + avro_generic_link_value_iface_t *next; +}; + + +static avro_value_iface_t * +avro_generic_link_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_link_value_iface_t *iface = + container_of(viface, avro_generic_link_value_iface_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_link_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_link_value_iface_t *iface = + container_of(viface, avro_generic_link_value_iface_t, parent.parent); + + if (avro_refcount_dec(&iface->refcount)) { + /* We don't keep a reference to the target + * implementation, since that would give us a reference + * cycle. */ + /* We do however keep a reference to the target + * schema, which we need to decrement before freeing + * the link */ + avro_schema_decref(iface->schema); + avro_freet(avro_generic_link_value_iface_t, iface); + } +} + + +static int +avro_generic_link_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_reset(self); +} + +static avro_type_t +avro_generic_link_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(viface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_type(self); +} + +static avro_schema_t +avro_generic_link_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(viface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_schema(self); +} + +static int +avro_generic_link_get_boolean(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_boolean(self, out); +} + +static int +avro_generic_link_get_bytes(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_bytes(self, buf, size); +} + +static int +avro_generic_link_grab_bytes(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_grab_bytes(self, dest); +} + +static int +avro_generic_link_get_double(const avro_value_iface_t *iface, + const void *vself, double *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_double(self, out); +} + +static int +avro_generic_link_get_float(const avro_value_iface_t *iface, + const void *vself, float *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_float(self, out); +} + +static int +avro_generic_link_get_int(const avro_value_iface_t *iface, + const void *vself, int32_t *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_int(self, out); +} + +static int +avro_generic_link_get_long(const avro_value_iface_t *iface, + const void *vself, int64_t *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_long(self, out); +} + +static int +avro_generic_link_get_null(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_null(self); +} + +static int +avro_generic_link_get_string(const avro_value_iface_t *iface, + const void *vself, const char **str, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_string(self, str, size); +} + +static int +avro_generic_link_grab_string(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_grab_string(self, dest); +} + +static int +avro_generic_link_get_enum(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_enum(self, out); +} + +static int +avro_generic_link_get_fixed(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_fixed(self, buf, size); +} + +static int +avro_generic_link_grab_fixed(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_grab_fixed(self, dest); +} + +static int +avro_generic_link_set_boolean(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_boolean(self, val); +} + +static int +avro_generic_link_set_bytes(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_bytes(self, buf, size); +} + +static int +avro_generic_link_give_bytes(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_give_bytes(self, buf); +} + +static int +avro_generic_link_set_double(const avro_value_iface_t *iface, + void *vself, double val) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_double(self, val); +} + +static int +avro_generic_link_set_float(const avro_value_iface_t *iface, + void *vself, float val) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_float(self, val); +} + +static int +avro_generic_link_set_int(const avro_value_iface_t *iface, + void *vself, int32_t val) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_int(self, val); +} + +static int +avro_generic_link_set_long(const avro_value_iface_t *iface, + void *vself, int64_t val) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_long(self, val); +} + +static int +avro_generic_link_set_null(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_null(self); +} + +static int +avro_generic_link_set_string(const avro_value_iface_t *iface, + void *vself, const char *str) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_string(self, str); +} + +static int +avro_generic_link_set_string_len(const avro_value_iface_t *iface, + void *vself, const char *str, size_t size) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_string_len(self, str, size); +} + +static int +avro_generic_link_give_string_len(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_give_string_len(self, buf); +} + +static int +avro_generic_link_set_enum(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_enum(self, val); +} + +static int +avro_generic_link_set_fixed(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_fixed(self, buf, size); +} + +static int +avro_generic_link_give_fixed(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_give_fixed(self, buf); +} + +static int +avro_generic_link_get_size(const avro_value_iface_t *iface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_size(self, size); +} + +static int +avro_generic_link_get_by_index(const avro_value_iface_t *iface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_by_index(self, index, child, name); +} + +static int +avro_generic_link_get_by_name(const avro_value_iface_t *iface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_by_name(self, name, child, index); +} + +static int +avro_generic_link_get_discriminant(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_discriminant(self, out); +} + +static int +avro_generic_link_get_current_branch(const avro_value_iface_t *iface, + const void *vself, avro_value_t *branch) +{ + AVRO_UNUSED(iface); + const avro_value_t *self = (const avro_value_t *) vself; + return avro_value_get_current_branch(self, branch); +} + +static int +avro_generic_link_append(const avro_value_iface_t *iface, + void *vself, avro_value_t *child_out, + size_t *new_index) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_append(self, child_out, new_index); +} + +static int +avro_generic_link_add(const avro_value_iface_t *iface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_add(self, key, child, index, is_new); +} + +static int +avro_generic_link_set_branch(const avro_value_iface_t *iface, + void *vself, int discriminant, + avro_value_t *branch) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + return avro_value_set_branch(self, discriminant, branch); +} + +static size_t +avro_generic_link_instance_size(const avro_value_iface_t *viface) +{ + AVRO_UNUSED(viface); + return sizeof(avro_value_t); +} + +static int +avro_generic_link_init(const avro_value_iface_t *viface, void *vself) +{ + int rval; + + avro_generic_link_value_iface_t *iface = + container_of(viface, avro_generic_link_value_iface_t, parent.parent); + + avro_value_t *self = (avro_value_t *) vself; + ssize_t target_instance_size = + avro_value_instance_size(iface->target_giface); + if (target_instance_size < 0) { + return EINVAL; + } + + self->iface = &iface->target_giface->parent; + + if (target_instance_size == 0) { + self->self = NULL; + } else { + self->self = avro_malloc(target_instance_size); + if (self->self == NULL) { + return ENOMEM; + } + } + + rval = avro_value_init(iface->target_giface, self->self); + if (rval != 0) { + avro_free(self->self, target_instance_size); + } + return rval; +} + +static void +avro_generic_link_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_value_t *self = (avro_value_t *) vself; + avro_generic_value_iface_t *target_giface = + container_of(self->iface, avro_generic_value_iface_t, parent); + size_t target_instance_size = avro_value_instance_size(target_giface); + avro_value_done(target_giface, self->self); + avro_free(self->self, target_instance_size); + self->iface = NULL; + self->self = NULL; +} + +static avro_generic_value_iface_t AVRO_GENERIC_LINK_CLASS = +{ + { + /* "class" methods */ + avro_generic_link_incref_iface, + avro_generic_link_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_link_reset, + avro_generic_link_get_type, + avro_generic_link_get_schema, + /* primitive getters */ + avro_generic_link_get_boolean, + avro_generic_link_get_bytes, + avro_generic_link_grab_bytes, + avro_generic_link_get_double, + avro_generic_link_get_float, + avro_generic_link_get_int, + avro_generic_link_get_long, + avro_generic_link_get_null, + avro_generic_link_get_string, + avro_generic_link_grab_string, + avro_generic_link_get_enum, + avro_generic_link_get_fixed, + avro_generic_link_grab_fixed, + /* primitive setters */ + avro_generic_link_set_boolean, + avro_generic_link_set_bytes, + avro_generic_link_give_bytes, + avro_generic_link_set_double, + avro_generic_link_set_float, + avro_generic_link_set_int, + avro_generic_link_set_long, + avro_generic_link_set_null, + avro_generic_link_set_string, + avro_generic_link_set_string_len, + avro_generic_link_give_string_len, + avro_generic_link_set_enum, + avro_generic_link_set_fixed, + avro_generic_link_give_fixed, + /* compound getters */ + avro_generic_link_get_size, + avro_generic_link_get_by_index, + avro_generic_link_get_by_name, + avro_generic_link_get_discriminant, + avro_generic_link_get_current_branch, + /* compound setters */ + avro_generic_link_append, + avro_generic_link_add, + avro_generic_link_set_branch + }, + avro_generic_link_instance_size, + avro_generic_link_init, + avro_generic_link_done +}; + +static avro_generic_link_value_iface_t * +avro_generic_link_class(avro_schema_t schema) +{ + if (!is_avro_link(schema)) { + avro_set_error("Expected link schema"); + return NULL; + } + + avro_generic_link_value_iface_t *iface = + (avro_generic_link_value_iface_t *) avro_new(avro_generic_link_value_iface_t); + if (iface == NULL) { + return NULL; + } + + iface->parent = AVRO_GENERIC_LINK_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + iface->next = NULL; + return iface; +} + + +/*----------------------------------------------------------------------- + * boolean + */ + +static int +avro_generic_boolean_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int *self = (int *) vself; + *self = 0; + return 0; +} + +static avro_type_t +avro_generic_boolean_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_BOOLEAN; +} + +static avro_schema_t +avro_generic_boolean_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_boolean(); +} + +static int +avro_generic_boolean_get(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const int *self = (const int *) vself; + *out = *self; + return 0; +} + +static int +avro_generic_boolean_set(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + int *self = (int *) vself; + *self = val; + return 0; +} + +static size_t +avro_generic_boolean_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(int); +} + +static int +avro_generic_boolean_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int *self = (int *) vself; + *self = 0; + return 0; +} + +static void +avro_generic_boolean_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_BOOLEAN_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_boolean_reset, + avro_generic_boolean_get_type, + avro_generic_boolean_get_schema, + /* primitive getters */ + avro_generic_boolean_get, + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + avro_generic_boolean_set, + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_boolean_instance_size, + avro_generic_boolean_init, + avro_generic_boolean_done +}; + +avro_value_iface_t * +avro_generic_boolean_class(void) +{ + return &AVRO_GENERIC_BOOLEAN_CLASS.parent; +} + +int +avro_generic_boolean_new(avro_value_t *value, int val) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_BOOLEAN_CLASS.parent, value)); + return avro_generic_boolean_set(value->iface, value->self, val); +} + +/*----------------------------------------------------------------------- + * bytes + */ + +static int +avro_generic_bytes_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_clear(self); + return 0; +} + +static avro_type_t +avro_generic_bytes_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_BYTES; +} + +static avro_schema_t +avro_generic_bytes_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_bytes(); +} + +static int +avro_generic_bytes_get(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_raw_string_t *self = (const avro_raw_string_t *) vself; + if (buf != NULL) { + *buf = avro_raw_string_get(self); + } + if (size != NULL) { + *size = avro_raw_string_length(self); + } + return 0; +} + +static int +avro_generic_bytes_grab(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_raw_string_t *self = (const avro_raw_string_t *) vself; + return avro_raw_string_grab(self, dest); +} + +static int +avro_generic_bytes_set(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + check_param(EINVAL, buf != NULL, "bytes contents"); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_set_length(self, buf, size); + return 0; +} + +static int +avro_generic_bytes_give(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_give(self, buf); + return 0; +} + +static size_t +avro_generic_bytes_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(avro_raw_string_t); +} + +static int +avro_generic_bytes_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_init(self); + return 0; +} + +static void +avro_generic_bytes_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_done(self); +} + +static avro_generic_value_iface_t AVRO_GENERIC_BYTES_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_bytes_reset, + avro_generic_bytes_get_type, + avro_generic_bytes_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + avro_generic_bytes_get, + avro_generic_bytes_grab, + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + avro_generic_bytes_set, + avro_generic_bytes_give, + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_bytes_instance_size, + avro_generic_bytes_init, + avro_generic_bytes_done +}; + +avro_value_iface_t * +avro_generic_bytes_class(void) +{ + return &AVRO_GENERIC_BYTES_CLASS.parent; +} + +int +avro_generic_bytes_new(avro_value_t *value, void *buf, size_t size) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_BYTES_CLASS.parent, value)); + return avro_generic_bytes_set(value->iface, value->self, buf, size); +} + +/*----------------------------------------------------------------------- + * double + */ + +static int +avro_generic_double_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + double *self = (double *) vself; + *self = 0.0; + return 0; +} + +static avro_type_t +avro_generic_double_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_DOUBLE; +} + +static avro_schema_t +avro_generic_double_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_double(); +} + +static int +avro_generic_double_get(const avro_value_iface_t *iface, + const void *vself, double *out) +{ + AVRO_UNUSED(iface); + const double *self = (const double *) vself; + *out = *self; + return 0; +} + +static int +avro_generic_double_set(const avro_value_iface_t *iface, + void *vself, double val) +{ + AVRO_UNUSED(iface); + double *self = (double *) vself; + *self = val; + return 0; +} + +static size_t +avro_generic_double_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(double); +} + +static int +avro_generic_double_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + double *self = (double *) vself; + *self = 0.0; + return 0; +} + +static void +avro_generic_double_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_DOUBLE_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_double_reset, + avro_generic_double_get_type, + avro_generic_double_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + avro_generic_double_get, + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + avro_generic_double_set, + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_double_instance_size, + avro_generic_double_init, + avro_generic_double_done +}; + +avro_value_iface_t * +avro_generic_double_class(void) +{ + return &AVRO_GENERIC_DOUBLE_CLASS.parent; +} + +int +avro_generic_double_new(avro_value_t *value, double val) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_DOUBLE_CLASS.parent, value)); + return avro_generic_double_set(value->iface, value->self, val); +} + +/*----------------------------------------------------------------------- + * float + */ + +static int +avro_generic_float_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + float *self = (float *) vself; + *self = 0.0f; + return 0; +} + +static avro_type_t +avro_generic_float_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_FLOAT; +} + +static avro_schema_t +avro_generic_float_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_float(); +} + +static int +avro_generic_float_get(const avro_value_iface_t *iface, + const void *vself, float *out) +{ + AVRO_UNUSED(iface); + const float *self = (const float *) vself; + *out = *self; + return 0; +} + +static int +avro_generic_float_set(const avro_value_iface_t *iface, + void *vself, float val) +{ + AVRO_UNUSED(iface); + float *self = (float *) vself; + *self = val; + return 0; +} + +static size_t +avro_generic_float_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(float); +} + +static int +avro_generic_float_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + float *self = (float *) vself; + *self = 0.0f; + return 0; +} + +static void +avro_generic_float_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_FLOAT_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_float_reset, + avro_generic_float_get_type, + avro_generic_float_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + avro_generic_float_get, + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + avro_generic_float_set, + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_float_instance_size, + avro_generic_float_init, + avro_generic_float_done +}; + +avro_value_iface_t * +avro_generic_float_class(void) +{ + return &AVRO_GENERIC_FLOAT_CLASS.parent; +} + +int +avro_generic_float_new(avro_value_t *value, float val) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_FLOAT_CLASS.parent, value)); + return avro_generic_float_set(value->iface, value->self, val); +} + +/*----------------------------------------------------------------------- + * int + */ + +static int +avro_generic_int_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int32_t *self = (int32_t *) vself; + *self = 0; + return 0; +} + +static avro_type_t +avro_generic_int_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_INT32; +} + +static avro_schema_t +avro_generic_int_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_int(); +} + +static int +avro_generic_int_get(const avro_value_iface_t *iface, + const void *vself, int32_t *out) +{ + AVRO_UNUSED(iface); + const int32_t *self = (const int32_t *) vself; + *out = *self; + return 0; +} + +static int +avro_generic_int_set(const avro_value_iface_t *iface, + void *vself, int32_t val) +{ + AVRO_UNUSED(iface); + int32_t *self = (int32_t *) vself; + *self = val; + return 0; +} + +static size_t +avro_generic_int_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(int32_t); +} + +static int +avro_generic_int_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int32_t *self = (int32_t *) vself; + *self = 0; + return 0; +} + +static void +avro_generic_int_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_INT_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_int_reset, + avro_generic_int_get_type, + avro_generic_int_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + avro_generic_int_get, + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + avro_generic_int_set, + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_int_instance_size, + avro_generic_int_init, + avro_generic_int_done +}; + +avro_value_iface_t * +avro_generic_int_class(void) +{ + return &AVRO_GENERIC_INT_CLASS.parent; +} + +int +avro_generic_int_new(avro_value_t *value, int32_t val) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_INT_CLASS.parent, value)); + return avro_generic_int_set(value->iface, value->self, val); +} + +/*----------------------------------------------------------------------- + * long + */ + +static int +avro_generic_long_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int64_t *self = (int64_t *) vself; + *self = 0; + return 0; +} + +static avro_type_t +avro_generic_long_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_INT64; +} + +static avro_schema_t +avro_generic_long_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_long(); +} + +static int +avro_generic_long_get(const avro_value_iface_t *iface, + const void *vself, int64_t *out) +{ + AVRO_UNUSED(iface); + const int64_t *self = (const int64_t *) vself; + *out = *self; + return 0; +} + +static int +avro_generic_long_set(const avro_value_iface_t *iface, + void *vself, int64_t val) +{ + AVRO_UNUSED(iface); + int64_t *self = (int64_t *) vself; + *self = val; + return 0; +} + +static size_t +avro_generic_long_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(int64_t); +} + +static int +avro_generic_long_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int64_t *self = (int64_t *) vself; + *self = 0; + return 0; +} + +static void +avro_generic_long_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_LONG_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_long_reset, + avro_generic_long_get_type, + avro_generic_long_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + avro_generic_long_get, + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + avro_generic_long_set, + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_long_instance_size, + avro_generic_long_init, + avro_generic_long_done +}; + +avro_value_iface_t * +avro_generic_long_class(void) +{ + return &AVRO_GENERIC_LONG_CLASS.parent; +} + +int +avro_generic_long_new(avro_value_t *value, int64_t val) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_LONG_CLASS.parent, value)); + return avro_generic_long_set(value->iface, value->self, val); +} + +/*----------------------------------------------------------------------- + * null + */ + +static int +avro_generic_null_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int *self = (int *) vself; + *self = 0; + return 0; +} + +static avro_type_t +avro_generic_null_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_NULL; +} + +static avro_schema_t +avro_generic_null_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_null(); +} + +static int +avro_generic_null_get(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return 0; +} + +static int +avro_generic_null_set(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return 0; +} + +static size_t +avro_generic_null_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(int); +} + +static int +avro_generic_null_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + int *self = (int *) vself; + *self = 0; + return 0; +} + +static void +avro_generic_null_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_NULL_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_null_reset, + avro_generic_null_get_type, + avro_generic_null_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + avro_generic_null_get, + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + avro_generic_null_set, + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_null_instance_size, + avro_generic_null_init, + avro_generic_null_done +}; + +avro_value_iface_t * +avro_generic_null_class(void) +{ + return &AVRO_GENERIC_NULL_CLASS.parent; +} + +int +avro_generic_null_new(avro_value_t *value) +{ + return avro_generic_value_new(&AVRO_GENERIC_NULL_CLASS.parent, value); +} + +/*----------------------------------------------------------------------- + * string + */ + +static int +avro_generic_string_reset(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_clear(self); + return 0; +} + +static avro_type_t +avro_generic_string_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_STRING; +} + +static avro_schema_t +avro_generic_string_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return avro_schema_string(); +} + +static int +avro_generic_string_get(const avro_value_iface_t *iface, + const void *vself, const char **str, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_raw_string_t *self = (const avro_raw_string_t *) vself; + const char *contents = (const char *) avro_raw_string_get(self); + + if (str != NULL) { + /* + * We can't return a NULL string, we have to return an + * *empty* string + */ + + *str = (contents == NULL)? "": contents; + } + if (size != NULL) { + /* raw_string's length includes the NUL terminator, + * unless it's empty */ + *size = (contents == NULL)? 1: avro_raw_string_length(self); + } + return 0; +} + +static int +avro_generic_string_grab(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_raw_string_t *self = (const avro_raw_string_t *) vself; + const char *contents = (const char *) avro_raw_string_get(self); + + if (contents == NULL) { + return avro_wrapped_buffer_new(dest, "", 1); + } else { + return avro_raw_string_grab(self, dest); + } +} + +static int +avro_generic_string_set(const avro_value_iface_t *iface, + void *vself, const char *val) +{ + AVRO_UNUSED(iface); + check_param(EINVAL, val != NULL, "string contents"); + + /* + * This raw_string method ensures that we copy the NUL + * terminator from val, and will include the NUL terminator in + * the raw_string's length, which is what we want. + */ + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_set(self, val); + return 0; +} + +static int +avro_generic_string_set_length(const avro_value_iface_t *iface, + void *vself, const char *val, size_t size) +{ + AVRO_UNUSED(iface); + check_param(EINVAL, val != NULL, "string contents"); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_set_length(self, val, size); + return 0; +} + +static int +avro_generic_string_give_length(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_give(self, buf); + return 0; +} + +static size_t +avro_generic_string_instance_size(const avro_value_iface_t *iface) +{ + AVRO_UNUSED(iface); + return sizeof(avro_raw_string_t); +} + +static int +avro_generic_string_init(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_init(self); + return 0; +} + +static void +avro_generic_string_done(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_raw_string_t *self = (avro_raw_string_t *) vself; + avro_raw_string_done(self); +} + +static avro_generic_value_iface_t AVRO_GENERIC_STRING_CLASS = +{ + { + /* "class" methods */ + NULL, /* incref_iface */ + NULL, /* decref_iface */ + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_string_reset, + avro_generic_string_get_type, + avro_generic_string_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + avro_generic_string_get, + avro_generic_string_grab, + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + avro_generic_string_set, + avro_generic_string_set_length, + avro_generic_string_give_length, + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_string_instance_size, + avro_generic_string_init, + avro_generic_string_done +}; + +avro_value_iface_t * +avro_generic_string_class(void) +{ + return &AVRO_GENERIC_STRING_CLASS.parent; +} + +int +avro_generic_string_new(avro_value_t *value, const char *str) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_STRING_CLASS.parent, value)); + return avro_generic_string_set(value->iface, value->self, str); +} + +int +avro_generic_string_new_length(avro_value_t *value, const char *str, size_t size) +{ + int rval; + check(rval, avro_generic_value_new(&AVRO_GENERIC_STRING_CLASS.parent, value)); + return avro_generic_string_set_length(value->iface, value->self, str, size); +} + + +/*----------------------------------------------------------------------- + * array + */ + +/* + * For generic arrays, we need to store the value implementation for the + * array's elements. + */ + +typedef struct avro_generic_array_value_iface { + avro_generic_value_iface_t parent; + volatile int refcount; + avro_schema_t schema; + avro_generic_value_iface_t *child_giface; +} avro_generic_array_value_iface_t; + +typedef struct avro_generic_array { + avro_raw_array_t array; +} avro_generic_array_t; + + +static avro_value_iface_t * +avro_generic_array_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_array_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + if (avro_refcount_dec(&iface->refcount)) { + avro_schema_decref(iface->schema); + avro_value_iface_decref(&iface->child_giface->parent); + avro_freet(avro_generic_array_value_iface_t, iface); + } +} + + +static void +avro_generic_array_free_elements(const avro_generic_value_iface_t *child_giface, + avro_generic_array_t *self) +{ + size_t i; + for (i = 0; i < avro_raw_array_size(&self->array); i++) { + void *child_self = avro_raw_array_get_raw(&self->array, i); + avro_value_done(child_giface, child_self); + } +} + +static int +avro_generic_array_reset(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + avro_generic_array_t *self = (avro_generic_array_t *) vself; + avro_generic_array_free_elements(iface->child_giface, self); + avro_raw_array_clear(&self->array); + return 0; +} + +static avro_type_t +avro_generic_array_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(viface); + AVRO_UNUSED(vself); + return AVRO_ARRAY; +} + +static avro_schema_t +avro_generic_array_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + const avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + AVRO_UNUSED(vself); + return iface->schema; +} + +static int +avro_generic_array_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_generic_array_t *self = (const avro_generic_array_t *) vself; + if (size != NULL) { + *size = avro_raw_array_size(&self->array); + } + return 0; +} + +static int +avro_generic_array_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + const avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + AVRO_UNUSED(name); + const avro_generic_array_t *self = (avro_generic_array_t *) vself; + if (index >= avro_raw_array_size(&self->array)) { + avro_set_error("Array index %" PRIsz " out of range", index); + return EINVAL; + } + child->iface = &iface->child_giface->parent; + child->self = avro_raw_array_get_raw(&self->array, index); + return 0; +} + +static int +avro_generic_array_append(const avro_value_iface_t *viface, + void *vself, avro_value_t *child, + size_t *new_index) +{ + int rval; + const avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + avro_generic_array_t *self = (avro_generic_array_t *) vself; + child->iface = &iface->child_giface->parent; + child->self = avro_raw_array_append(&self->array); + if (child->self == NULL) { + avro_set_error("Couldn't expand array"); + return ENOMEM; + } + check(rval, avro_value_init(iface->child_giface, child->self)); + if (new_index != NULL) { + *new_index = avro_raw_array_size(&self->array) - 1; + } + return 0; +} + +static size_t +avro_generic_array_instance_size(const avro_value_iface_t *viface) +{ + AVRO_UNUSED(viface); + return sizeof(avro_generic_array_t); +} + +static int +avro_generic_array_init(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + avro_generic_array_t *self = (avro_generic_array_t *) vself; + + size_t child_size = avro_value_instance_size(iface->child_giface); + avro_raw_array_init(&self->array, child_size); + return 0; +} + +static void +avro_generic_array_done(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_array_value_iface_t *iface = + container_of(viface, avro_generic_array_value_iface_t, parent); + avro_generic_array_t *self = (avro_generic_array_t *) vself; + avro_generic_array_free_elements(iface->child_giface, self); + avro_raw_array_done(&self->array); +} + +static avro_generic_value_iface_t AVRO_GENERIC_ARRAY_CLASS = +{ +{ + /* "class" methods */ + avro_generic_array_incref_iface, + avro_generic_array_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_array_reset, + avro_generic_array_get_type, + avro_generic_array_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + avro_generic_array_get_size, + avro_generic_array_get_by_index, + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + avro_generic_array_append, + NULL, /* add */ + NULL /* set_branch */ +}, + avro_generic_array_instance_size, + avro_generic_array_init, + avro_generic_array_done +}; + +static avro_generic_value_iface_t * +avro_generic_array_class(avro_schema_t schema, memoize_state_t *state) +{ + avro_schema_t child_schema = avro_schema_array_items(schema); + avro_generic_value_iface_t *child_giface = + avro_generic_class_from_schema_memoized(child_schema, state); + if (child_giface == NULL) { + return NULL; + } + + ssize_t child_size = avro_value_instance_size(child_giface); + if (child_size < 0) { + avro_set_error("Array item class must provide instance_size"); + avro_value_iface_decref(&child_giface->parent); + return NULL; + } + + avro_generic_array_value_iface_t *iface = + (avro_generic_array_value_iface_t *) avro_new(avro_generic_array_value_iface_t); + if (iface == NULL) { + avro_value_iface_decref(&child_giface->parent); + return NULL; + } + + /* + * TODO: Maybe check that schema.items matches + * child_iface.get_schema? + */ + + iface->parent = AVRO_GENERIC_ARRAY_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + iface->child_giface = child_giface; + return &iface->parent; +} + + +/*----------------------------------------------------------------------- + * enum + */ + +typedef struct avro_generic_enum_value_iface { + avro_generic_value_iface_t parent; + volatile int refcount; + avro_schema_t schema; +} avro_generic_enum_value_iface_t; + + +static avro_value_iface_t * +avro_generic_enum_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_enum_value_iface_t *iface = + (avro_generic_enum_value_iface_t *) viface; + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_enum_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_enum_value_iface_t *iface = + (avro_generic_enum_value_iface_t *) viface; + if (avro_refcount_dec(&iface->refcount)) { + avro_schema_decref(iface->schema); + avro_freet(avro_generic_enum_value_iface_t, iface); + } +} + +static int +avro_generic_enum_reset(const avro_value_iface_t *viface, void *vself) +{ + AVRO_UNUSED(viface); + int *self = (int *) vself; + *self = 0; + return 0; +} + +static avro_type_t +avro_generic_enum_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_ENUM; +} + +static avro_schema_t +avro_generic_enum_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + const avro_generic_enum_value_iface_t *iface = + container_of(viface, avro_generic_enum_value_iface_t, parent); + AVRO_UNUSED(vself); + return iface->schema; +} + +static int +avro_generic_enum_get(const avro_value_iface_t *viface, + const void *vself, int *out) +{ + AVRO_UNUSED(viface); + const int *self = (const int *) vself; + *out = *self; + return 0; +} + +static int +avro_generic_enum_set(const avro_value_iface_t *viface, + void *vself, int val) +{ + AVRO_UNUSED(viface); + int *self = (int *) vself; + *self = val; + return 0; +} + +static size_t +avro_generic_enum_instance_size(const avro_value_iface_t *viface) +{ + AVRO_UNUSED(viface); + return sizeof(int); +} + +static int +avro_generic_enum_init(const avro_value_iface_t *viface, void *vself) +{ + AVRO_UNUSED(viface); + int *self = (int *) vself; + *self = 0; + return 0; +} + +static void +avro_generic_enum_done(const avro_value_iface_t *viface, void *vself) +{ + AVRO_UNUSED(viface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_ENUM_CLASS = +{ + { + /* "class" methods */ + avro_generic_enum_incref_iface, + avro_generic_enum_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_enum_reset, + avro_generic_enum_get_type, + avro_generic_enum_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + avro_generic_enum_get, + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + avro_generic_enum_set, + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_enum_instance_size, + avro_generic_enum_init, + avro_generic_enum_done +}; + +static avro_generic_value_iface_t * +avro_generic_enum_class(avro_schema_t schema) +{ + avro_generic_enum_value_iface_t *iface = + (avro_generic_enum_value_iface_t *) avro_new(avro_generic_enum_value_iface_t); + if (iface == NULL) { + return NULL; + } + + iface->parent = AVRO_GENERIC_ENUM_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + return &iface->parent; +} + + +/*----------------------------------------------------------------------- + * fixed + */ + +typedef struct avro_generic_fixed_value_iface { + avro_generic_value_iface_t parent; + volatile int refcount; + avro_schema_t schema; + size_t data_size; +} avro_generic_fixed_value_iface_t; + + +static avro_value_iface_t * +avro_generic_fixed_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_fixed_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + if (avro_refcount_dec(&iface->refcount)) { + avro_schema_decref(iface->schema); + avro_freet(avro_generic_fixed_value_iface_t, iface); + } +} + +static int +avro_generic_fixed_reset(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + memset(vself, 0, iface->data_size); + return 0; +} + +static avro_type_t +avro_generic_fixed_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + AVRO_UNUSED(vself); + return AVRO_FIXED; +} + +static avro_schema_t +avro_generic_fixed_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + AVRO_UNUSED(vself); + return iface->schema; +} + +static int +avro_generic_fixed_get(const avro_value_iface_t *viface, + const void *vself, const void **buf, size_t *size) +{ + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + if (buf != NULL) { + *buf = vself; + } + if (size != NULL) { + *size = iface->data_size; + } + return 0; +} + +static int +avro_generic_fixed_grab(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + return avro_wrapped_buffer_new(dest, vself, iface->data_size); +} + +static int +avro_generic_fixed_set(const avro_value_iface_t *viface, + void *vself, void *buf, size_t size) +{ + check_param(EINVAL, buf != NULL, "fixed contents"); + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + if (size != iface->data_size) { + avro_set_error("Invalid data size in set_fixed"); + return EINVAL; + } + memcpy(vself, buf, size); + return 0; +} + +static int +avro_generic_fixed_give(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *buf) +{ + int rval = avro_generic_fixed_set + (viface, vself, (void *) buf->buf, buf->size); + avro_wrapped_buffer_free(buf); + return rval; +} + +static size_t +avro_generic_fixed_instance_size(const avro_value_iface_t *viface) +{ + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + return iface->data_size; +} + +static int +avro_generic_fixed_init(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_fixed_value_iface_t *iface = + container_of(viface, avro_generic_fixed_value_iface_t, parent); + memset(vself, 0, iface->data_size); + return 0; +} + +static void +avro_generic_fixed_done(const avro_value_iface_t *viface, void *vself) +{ + AVRO_UNUSED(viface); + AVRO_UNUSED(vself); +} + +static avro_generic_value_iface_t AVRO_GENERIC_FIXED_CLASS = +{ + { + /* "class" methods */ + avro_generic_fixed_incref_iface, + avro_generic_fixed_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_fixed_reset, + avro_generic_fixed_get_type, + avro_generic_fixed_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + avro_generic_fixed_get, + avro_generic_fixed_grab, + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + avro_generic_fixed_set, + avro_generic_fixed_give, + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_fixed_instance_size, + avro_generic_fixed_init, + avro_generic_fixed_done +}; + +static avro_generic_value_iface_t * +avro_generic_fixed_class(avro_schema_t schema) +{ + avro_generic_fixed_value_iface_t *iface = + (avro_generic_fixed_value_iface_t *) avro_new(avro_generic_fixed_value_iface_t); + if (iface == NULL) { + return NULL; + } + + iface->parent = AVRO_GENERIC_FIXED_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + iface->data_size = avro_schema_fixed_size(schema); + return &iface->parent; +} + + +/*----------------------------------------------------------------------- + * map + */ + +/* + * For generic maps, we need to store the value implementation for the + * map's elements. + */ + +typedef struct avro_generic_map_value_iface { + avro_generic_value_iface_t parent; + volatile int refcount; + avro_schema_t schema; + avro_generic_value_iface_t *child_giface; +} avro_generic_map_value_iface_t; + +typedef struct avro_generic_map { + avro_raw_map_t map; +} avro_generic_map_t; + + +static avro_value_iface_t * +avro_generic_map_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_map_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + if (avro_refcount_dec(&iface->refcount)) { + avro_schema_decref(iface->schema); + avro_value_iface_decref(&iface->child_giface->parent); + avro_freet(avro_generic_map_value_iface_t, iface); + } +} + + +static void +avro_generic_map_free_elements(const avro_generic_value_iface_t *child_giface, + avro_generic_map_t *self) +{ + size_t i; + for (i = 0; i < avro_raw_map_size(&self->map); i++) { + void *child_self = avro_raw_map_get_raw(&self->map, i); + avro_value_done(child_giface, child_self); + } +} + +static int +avro_generic_map_reset(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + avro_generic_map_t *self = (avro_generic_map_t *) vself; + avro_generic_map_free_elements(iface->child_giface, self); + avro_raw_map_clear(&self->map); + return 0; +} + +static avro_type_t +avro_generic_map_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(viface); + AVRO_UNUSED(vself); + return AVRO_MAP; +} + +static avro_schema_t +avro_generic_map_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + AVRO_UNUSED(vself); + return iface->schema; +} + +static int +avro_generic_map_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_generic_map_t *self = (const avro_generic_map_t *) vself; + if (size != NULL) { + *size = avro_raw_map_size(&self->map); + } + return 0; +} + +static int +avro_generic_map_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + const avro_generic_map_t *self = (const avro_generic_map_t *) vself; + if (index >= avro_raw_map_size(&self->map)) { + avro_set_error("Map index %" PRIsz " out of range", index); + return EINVAL; + } + child->iface = &iface->child_giface->parent; + child->self = avro_raw_map_get_raw(&self->map, index); + if (name != NULL) { + *name = avro_raw_map_get_key(&self->map, index); + } + return 0; +} + +static int +avro_generic_map_get_by_name(const avro_value_iface_t *viface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + const avro_generic_map_t *self = (const avro_generic_map_t *) vself; + child->iface = &iface->child_giface->parent; + child->self = avro_raw_map_get(&self->map, name, index); + if (child->self == NULL) { + avro_set_error("No map element named %s", name); + return EINVAL; + } + return 0; +} + +static int +avro_generic_map_add(const avro_value_iface_t *viface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + int rval; + avro_generic_map_t *self = (avro_generic_map_t *) vself; + child->iface = &iface->child_giface->parent; + rval = avro_raw_map_get_or_create(&self->map, key, + &child->self, index); + if (rval < 0) { + return -rval; + } + if (is_new != NULL) { + *is_new = rval; + } + if (rval) { + check(rval, avro_value_init(iface->child_giface, child->self)); + } + return 0; +} + +static size_t +avro_generic_map_instance_size(const avro_value_iface_t *viface) +{ + AVRO_UNUSED(viface); + return sizeof(avro_generic_map_t); +} + +static int +avro_generic_map_init(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + avro_generic_map_t *self = (avro_generic_map_t *) vself; + + size_t child_size = avro_value_instance_size(iface->child_giface); + avro_raw_map_init(&self->map, child_size); + return 0; +} + +static void +avro_generic_map_done(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_map_value_iface_t *iface = + container_of(viface, avro_generic_map_value_iface_t, parent); + avro_generic_map_t *self = (avro_generic_map_t *) vself; + avro_generic_map_free_elements(iface->child_giface, self); + avro_raw_map_done(&self->map); +} + +static avro_generic_value_iface_t AVRO_GENERIC_MAP_CLASS = +{ + { + /* "class" methods */ + avro_generic_map_incref_iface, + avro_generic_map_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_map_reset, + avro_generic_map_get_type, + avro_generic_map_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + avro_generic_map_get_size, + avro_generic_map_get_by_index, + avro_generic_map_get_by_name, + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + avro_generic_map_add, + NULL /* set_branch */ + }, + avro_generic_map_instance_size, + avro_generic_map_init, + avro_generic_map_done +}; + +static avro_generic_value_iface_t * +avro_generic_map_class(avro_schema_t schema, memoize_state_t *state) +{ + avro_schema_t child_schema = avro_schema_array_items(schema); + avro_generic_value_iface_t *child_giface = + avro_generic_class_from_schema_memoized(child_schema, state); + if (child_giface == NULL) { + return NULL; + } + + ssize_t child_size = avro_value_instance_size(child_giface); + if (child_size < 0) { + avro_set_error("Map value class must provide instance_size"); + avro_value_iface_decref(&child_giface->parent); + return NULL; + } + + avro_generic_map_value_iface_t *iface = + (avro_generic_map_value_iface_t *) avro_new(avro_generic_map_value_iface_t); + if (iface == NULL) { + avro_value_iface_decref(&child_giface->parent); + return NULL; + } + + /* + * TODO: Maybe check that schema.items matches + * child_iface.get_schema? + */ + + iface->parent = AVRO_GENERIC_MAP_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + iface->child_giface = child_giface; + return &iface->parent; +} + + +/*----------------------------------------------------------------------- + * record + */ + +#ifndef DEBUG_FIELD_OFFSETS +#define DEBUG_FIELD_OFFSETS 0 +#endif + +#if DEBUG_FIELD_OFFSETS +#include <stdio.h> +#endif + +/* + * For generic records, we need to store the value implementation for + * each field. We also need to store an offset for each field, since + * we're going to store the contents of each field directly in the + * record, rather than via pointers. + */ + +typedef struct avro_generic_record_value_iface { + avro_generic_value_iface_t parent; + volatile int refcount; + avro_schema_t schema; + + /** The total size of each value struct for this record. */ + size_t instance_size; + + /** The number of fields in this record. Yes, we could get this + * from schema, but this is easier. */ + size_t field_count; + + /** The offset of each field within the record struct. */ + size_t *field_offsets; + + /** The value implementation for each field. */ + avro_generic_value_iface_t **field_ifaces; +} avro_generic_record_value_iface_t; + +typedef struct avro_generic_record { + /* The rest of the struct is taken up by the inline storage + * needed for each field. */ +} avro_generic_record_t; + + +/** Return a pointer to the given field within a record struct. */ +#define avro_generic_record_field(iface, rec, index) \ + (((char *) (rec)) + (iface)->field_offsets[(index)]) + + +static avro_value_iface_t * +avro_generic_record_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_record_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + + if (avro_refcount_dec(&iface->refcount)) { + size_t i; + for (i = 0; i < iface->field_count; i++) { + avro_value_iface_decref(&iface->field_ifaces[i]->parent); + } + + avro_schema_decref(iface->schema); + avro_free(iface->field_offsets, + sizeof(size_t) * iface->field_count); + avro_free(iface->field_ifaces, + sizeof(avro_generic_value_iface_t *) * iface->field_count); + + avro_freet(avro_generic_record_value_iface_t, iface); + } +} + + +static int +avro_generic_record_reset(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + int rval; + avro_generic_record_t *self = (avro_generic_record_t *) vself; + size_t i; + for (i = 0; i < iface->field_count; i++) { + avro_value_t value = { + &iface->field_ifaces[i]->parent, + avro_generic_record_field(iface, self, i) + }; + check(rval, avro_value_reset(&value)); + } + return 0; +} + +static avro_type_t +avro_generic_record_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(viface); + AVRO_UNUSED(vself); + return AVRO_RECORD; +} + +static avro_schema_t +avro_generic_record_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + AVRO_UNUSED(vself); + return iface->schema; +} + +static int +avro_generic_record_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + AVRO_UNUSED(vself); + if (size != NULL) { + *size = iface->field_count; + } + return 0; +} + +static int +avro_generic_record_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + const avro_generic_record_t *self = (const avro_generic_record_t *) vself; + if (index >= iface->field_count) { + avro_set_error("Field index %" PRIsz " out of range", index); + return EINVAL; + } + child->iface = &iface->field_ifaces[index]->parent; + child->self = avro_generic_record_field(iface, self, index); + + /* + * Grab the field name from the schema if asked for. + */ + if (name != NULL) { + avro_schema_t schema = iface->schema; + *name = avro_schema_record_field_name(schema, index); + } + + return 0; +} + +static int +avro_generic_record_get_by_name(const avro_value_iface_t *viface, + const void *vself, const char *name, + avro_value_t *child, size_t *index_out) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + const avro_generic_record_t *self = (const avro_generic_record_t *) vself; + + avro_schema_t schema = iface->schema; + int index = avro_schema_record_field_get_index(schema, name); + if (index < 0) { + avro_set_error("Unknown record field %s", name); + return EINVAL; + } + + child->iface = &iface->field_ifaces[index]->parent; + child->self = avro_generic_record_field(iface, self, index); + if (index_out != NULL) { + *index_out = index; + } + return 0; +} + +static size_t +avro_generic_record_instance_size(const avro_value_iface_t *viface) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + return iface->instance_size; +} + +static int +avro_generic_record_init(const avro_value_iface_t *viface, void *vself) +{ + int rval; + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + avro_generic_record_t *self = (avro_generic_record_t *) vself; + + /* Initialize each field */ + size_t i; + for (i = 0; i < iface->field_count; i++) { + check(rval, avro_value_init + (iface->field_ifaces[i], + avro_generic_record_field(iface, self, i))); + } + + return 0; +} + +static void +avro_generic_record_done(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_record_value_iface_t *iface = + container_of(viface, avro_generic_record_value_iface_t, parent); + avro_generic_record_t *self = (avro_generic_record_t *) vself; + size_t i; + for (i = 0; i < iface->field_count; i++) { + avro_value_done(iface->field_ifaces[i], + avro_generic_record_field(iface, self, i)); + } +} + +static avro_generic_value_iface_t AVRO_GENERIC_RECORD_CLASS = +{ + { + /* "class" methods */ + avro_generic_record_incref_iface, + avro_generic_record_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_record_reset, + avro_generic_record_get_type, + avro_generic_record_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + avro_generic_record_get_size, + avro_generic_record_get_by_index, + avro_generic_record_get_by_name, + NULL, /* get_discriminant */ + NULL, /* get_current_branch */ + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + NULL /* set_branch */ + }, + avro_generic_record_instance_size, + avro_generic_record_init, + avro_generic_record_done +}; + +static avro_generic_value_iface_t * +avro_generic_record_class(avro_schema_t schema, memoize_state_t *state) +{ + avro_generic_record_value_iface_t *iface = + (avro_generic_record_value_iface_t *) avro_new(avro_generic_record_value_iface_t); + if (iface == NULL) { + return NULL; + } + + memset(iface, 0, sizeof(avro_generic_record_value_iface_t)); + iface->parent = AVRO_GENERIC_RECORD_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + + iface->field_count = avro_schema_record_size(schema); + size_t field_offsets_size = + sizeof(size_t) * iface->field_count; + size_t field_ifaces_size = + sizeof(avro_generic_value_iface_t *) * iface->field_count; + + if (iface->field_count == 0) { + iface->field_offsets = NULL; + iface->field_ifaces = NULL; + } else { + iface->field_offsets = (size_t *) avro_malloc(field_offsets_size); + if (iface->field_offsets == NULL) { + goto error; + } + + iface->field_ifaces = (avro_generic_value_iface_t **) avro_malloc(field_ifaces_size); + if (iface->field_ifaces == NULL) { + goto error; + } + } + + size_t next_offset = sizeof(avro_generic_record_t); +#if DEBUG_FIELD_OFFSETS + fprintf(stderr, " Record %s\n Header: Offset 0, size %" PRIsz "\n", + avro_schema_type_name(schema), + sizeof(avro_generic_record_t)); +#endif + size_t i; + for (i = 0; i < iface->field_count; i++) { +#if DEBUG_FIELD_OFFSETS + fprintf(stderr, " Field %" PRIsz ":\n", i); +#endif + avro_schema_t field_schema = + avro_schema_record_field_get_by_index(schema, i); +#if DEBUG_FIELD_OFFSETS + fprintf(stderr, " Schema %s\n", + avro_schema_type_name(field_schema)); +#endif + + iface->field_offsets[i] = next_offset; + + iface->field_ifaces[i] = + avro_generic_class_from_schema_memoized(field_schema, state); + if (iface->field_ifaces[i] == NULL) { + goto error; + } + + ssize_t field_size = + avro_value_instance_size(iface->field_ifaces[i]); + if (field_size < 0) { + avro_set_error("Record field class must provide instance_size"); + goto error; + } + +#if DEBUG_FIELD_OFFSETS + fprintf(stderr, " Offset %" PRIsz ", size %" PRIsz "\n", + next_offset, field_size); +#endif + next_offset += field_size; + } + + iface->instance_size = next_offset; +#if DEBUG_FIELD_OFFSETS + fprintf(stderr, " TOTAL SIZE: %" PRIsz "\n", next_offset); +#endif + + return &iface->parent; + +error: + avro_schema_decref(iface->schema); + if (iface->field_offsets != NULL) { + avro_free(iface->field_offsets, field_offsets_size); + } + if (iface->field_ifaces != NULL) { + for (i = 0; i < iface->field_count; i++) { + if (iface->field_ifaces[i] != NULL) { + avro_value_iface_decref(&iface->field_ifaces[i]->parent); + } + } + avro_free(iface->field_ifaces, field_ifaces_size); + } + avro_freet(avro_generic_record_value_iface_t, iface); + return NULL; +} + + +/*----------------------------------------------------------------------- + * union + */ + +#ifndef DEBUG_BRANCHES_OFFSETS +#define DEBUG_BRANCHES_OFFSETS 0 +#endif + +#if DEBUG_BRANCHES_OFFSETS +#include <stdio.h> +#endif + +/* + * For generic unions, we need to store the value implementation for + * each branch, just like for generic records. However, for unions, we + * can only have one branch active at a time, so we can reuse the space + * in the union struct, just like is done with C unions. + */ + +typedef struct avro_generic_union_value_iface { + avro_generic_value_iface_t parent; + volatile int refcount; + avro_schema_t schema; + + /** The total size of each value struct for this union. */ + size_t instance_size; + + /** The number of branches in this union. Yes, we could get + * this from schema, but this is easier. */ + size_t branch_count; + + /** The value implementation for each branch. */ + avro_generic_value_iface_t **branch_ifaces; +} avro_generic_union_value_iface_t; + +typedef struct avro_generic_union { + /** The currently active branch of the union. -1 if no branch + * is selected. */ + int discriminant; + + /* The rest of the struct is taken up by the inline storage + * needed for the active branch. */ +} avro_generic_union_t; + + +/** Return the child interface for the active branch. */ +#define avro_generic_union_branch_giface(iface, _union) \ + ((iface)->branch_ifaces[(_union)->discriminant]) +#define avro_generic_union_branch_iface(iface, _union) \ + (&(avro_generic_union_branch_giface((iface), (_union)))->parent) + +/** Return a pointer to the active branch within a union struct. */ +#define avro_generic_union_branch(_union) \ + (((char *) (_union)) + sizeof(avro_generic_union_t)) + + +static avro_value_iface_t * +avro_generic_union_incref_iface(avro_value_iface_t *viface) +{ + avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +avro_generic_union_decref_iface(avro_value_iface_t *viface) +{ + avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + + if (avro_refcount_dec(&iface->refcount)) { + size_t i; + for (i = 0; i < iface->branch_count; i++) { + avro_value_iface_decref(&iface->branch_ifaces[i]->parent); + } + + avro_schema_decref(iface->schema); + avro_free(iface->branch_ifaces, + sizeof(avro_generic_value_iface_t *) * iface->branch_count); + + avro_freet(avro_generic_union_value_iface_t, iface); + } +} + + +static int +avro_generic_union_reset(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + avro_generic_union_t *self = (avro_generic_union_t *) vself; + /* Keep the same branch selected, for the common case that we're + * about to reuse it. */ + if (self->discriminant >= 0) { +#if DEBUG_BRANCHES + fprintf(stderr, "Resetting branch %d\n", + self->discriminant); +#endif + avro_value_t value = { + avro_generic_union_branch_iface(iface, self), + avro_generic_union_branch(self) + }; + return avro_value_reset(&value); + } + return 0; +} + +static avro_type_t +avro_generic_union_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(viface); + AVRO_UNUSED(vself); + return AVRO_UNION; +} + +static avro_schema_t +avro_generic_union_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + const avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + AVRO_UNUSED(vself); + return iface->schema; +} + +static int +avro_generic_union_get_discriminant(const avro_value_iface_t *viface, + const void *vself, int *out) +{ + AVRO_UNUSED(viface); + const avro_generic_union_t *self = (const avro_generic_union_t *) vself; + *out = self->discriminant; + return 0; +} + +static int +avro_generic_union_get_current_branch(const avro_value_iface_t *viface, + const void *vself, avro_value_t *branch) +{ + const avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + const avro_generic_union_t *self = (const avro_generic_union_t *) vself; + if (self->discriminant < 0) { + avro_set_error("Union has no selected branch"); + return EINVAL; + } + branch->iface = avro_generic_union_branch_iface(iface, self); + branch->self = avro_generic_union_branch(self); + return 0; +} + +static int +avro_generic_union_set_branch(const avro_value_iface_t *viface, + void *vself, int discriminant, + avro_value_t *branch) +{ + const avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + int rval; + avro_generic_union_t *self = (avro_generic_union_t *) vself; + +#if DEBUG_BRANCHES + fprintf(stderr, "Selecting branch %d (was %d)\n", + discriminant, self->discriminant); +#endif + + /* + * If the new desired branch is different than the currently + * active one, then finalize the old branch and initialize the + * new one. + */ + if (self->discriminant != discriminant) { + if (self->discriminant >= 0) { +#if DEBUG_BRANCHES + fprintf(stderr, "Finalizing branch %d\n", + self->discriminant); +#endif + avro_value_done + (avro_generic_union_branch_giface(iface, self), + avro_generic_union_branch(self)); + } + self->discriminant = discriminant; + if (discriminant >= 0) { +#if DEBUG_BRANCHES + fprintf(stderr, "Initializing branch %d\n", + self->discriminant); +#endif + check(rval, avro_value_init + (avro_generic_union_branch_giface(iface, self), + avro_generic_union_branch(self))); + } + } + + if (branch != NULL) { + branch->iface = avro_generic_union_branch_iface(iface, self); + branch->self = avro_generic_union_branch(self); + } + + return 0; +} + +static size_t +avro_generic_union_instance_size(const avro_value_iface_t *viface) +{ + const avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + return iface->instance_size; +} + +static int +avro_generic_union_init(const avro_value_iface_t *viface, void *vself) +{ + AVRO_UNUSED(viface); + avro_generic_union_t *self = (avro_generic_union_t *) vself; + self->discriminant = -1; + return 0; +} + +static void +avro_generic_union_done(const avro_value_iface_t *viface, void *vself) +{ + const avro_generic_union_value_iface_t *iface = + container_of(viface, avro_generic_union_value_iface_t, parent); + avro_generic_union_t *self = (avro_generic_union_t *) vself; + if (self->discriminant >= 0) { +#if DEBUG_BRANCHES + fprintf(stderr, "Finalizing branch %d\n", + self->discriminant); +#endif + avro_value_done + (avro_generic_union_branch_giface(iface, self), + avro_generic_union_branch(self)); + self->discriminant = -1; + } +} + +static avro_generic_value_iface_t AVRO_GENERIC_UNION_CLASS = +{ + { + /* "class" methods */ + avro_generic_union_incref_iface, + avro_generic_union_decref_iface, + /* general "instance" methods */ + avro_generic_value_incref, + avro_generic_value_decref, + avro_generic_union_reset, + avro_generic_union_get_type, + avro_generic_union_get_schema, + /* primitive getters */ + NULL, /* get_boolean */ + NULL, /* get_bytes */ + NULL, /* grab_bytes */ + NULL, /* get_double */ + NULL, /* get_float */ + NULL, /* get_int */ + NULL, /* get_long */ + NULL, /* get_null */ + NULL, /* get_string */ + NULL, /* grab_string */ + NULL, /* get_enum */ + NULL, /* get_fixed */ + NULL, /* grab_fixed */ + /* primitive setters */ + NULL, /* set_boolean */ + NULL, /* set_bytes */ + NULL, /* give_bytes */ + NULL, /* set_double */ + NULL, /* set_float */ + NULL, /* set_int */ + NULL, /* set_long */ + NULL, /* set_null */ + NULL, /* set_string */ + NULL, /* set_string_length */ + NULL, /* give_string_length */ + NULL, /* set_enum */ + NULL, /* set_fixed */ + NULL, /* give_fixed */ + /* compound getters */ + NULL, /* get_size */ + NULL, /* get_by_index */ + NULL, /* get_by_name */ + avro_generic_union_get_discriminant, + avro_generic_union_get_current_branch, + /* compound setters */ + NULL, /* append */ + NULL, /* add */ + avro_generic_union_set_branch + }, + avro_generic_union_instance_size, + avro_generic_union_init, + avro_generic_union_done +}; + +static avro_generic_value_iface_t * +avro_generic_union_class(avro_schema_t schema, memoize_state_t *state) +{ + avro_generic_union_value_iface_t *iface = + (avro_generic_union_value_iface_t *) avro_new(avro_generic_union_value_iface_t); + if (iface == NULL) { + return NULL; + } + + memset(iface, 0, sizeof(avro_generic_union_value_iface_t)); + iface->parent = AVRO_GENERIC_UNION_CLASS; + iface->refcount = 1; + iface->schema = avro_schema_incref(schema); + + iface->branch_count = avro_schema_union_size(schema); + size_t branch_ifaces_size = + sizeof(avro_generic_value_iface_t *) * iface->branch_count; + + iface->branch_ifaces = (avro_generic_value_iface_t **) avro_malloc(branch_ifaces_size); + if (iface->branch_ifaces == NULL) { + goto error; + } + + size_t max_branch_size = 0; + size_t i; + for (i = 0; i < iface->branch_count; i++) { + avro_schema_t branch_schema = + avro_schema_union_branch(schema, i); + + iface->branch_ifaces[i] = + avro_generic_class_from_schema_memoized(branch_schema, state); + if (iface->branch_ifaces[i] == NULL) { + goto error; + } + + ssize_t branch_size = + avro_value_instance_size(iface->branch_ifaces[i]); + if (branch_size < 0) { + avro_set_error("Union branch class must provide instance_size"); + goto error; + } + +#if DEBUG_BRANCHES + fprintf(stderr, "Branch %" PRIsz ", size %" PRIsz "\n", + i, branch_size); +#endif + + if ((size_t)branch_size > max_branch_size) { + max_branch_size = (size_t)branch_size; + } + } + + iface->instance_size = + sizeof(avro_generic_union_t) + max_branch_size; +#if DEBUG_BRANCHES + fprintf(stderr, "MAX BRANCH SIZE: %" PRIsz "\n", max_branch_size); +#endif + + return &iface->parent; + +error: + avro_schema_decref(iface->schema); + if (iface->branch_ifaces != NULL) { + for (i = 0; i < iface->branch_count; i++) { + if (iface->branch_ifaces[i] != NULL) { + avro_value_iface_decref(&iface->branch_ifaces[i]->parent); + } + } + avro_free(iface->branch_ifaces, branch_ifaces_size); + } + avro_freet(avro_generic_union_value_iface_t, iface); + return NULL; +} + + +/*----------------------------------------------------------------------- + * Schema type dispatcher + */ + +static avro_generic_value_iface_t * +avro_generic_class_from_schema_memoized(avro_schema_t schema, + memoize_state_t *state) +{ + /* + * If we've already instantiated a value class for this schema, + * just return it. + */ + + avro_generic_value_iface_t *result = NULL; + if (avro_memoize_get(&state->mem, schema, NULL, (void **) &result)) { + avro_value_iface_incref(&result->parent); + return result; + } + + /* + * Otherwise instantiate the value class based on the schema + * type. + */ + + switch (schema->type) { + case AVRO_BOOLEAN: + result = &AVRO_GENERIC_BOOLEAN_CLASS; + break; + case AVRO_BYTES: + result = &AVRO_GENERIC_BYTES_CLASS; + break; + case AVRO_DOUBLE: + result = &AVRO_GENERIC_DOUBLE_CLASS; + break; + case AVRO_FLOAT: + result = &AVRO_GENERIC_FLOAT_CLASS; + break; + case AVRO_INT32: + result = &AVRO_GENERIC_INT_CLASS; + break; + case AVRO_INT64: + result = &AVRO_GENERIC_LONG_CLASS; + break; + case AVRO_NULL: + result = &AVRO_GENERIC_NULL_CLASS; + break; + case AVRO_STRING: + result = &AVRO_GENERIC_STRING_CLASS; + break; + + case AVRO_ARRAY: + result = avro_generic_array_class(schema, state); + break; + case AVRO_ENUM: + result = avro_generic_enum_class(schema); + break; + case AVRO_FIXED: + result = avro_generic_fixed_class(schema); + break; + case AVRO_MAP: + result = avro_generic_map_class(schema, state); + break; + case AVRO_RECORD: + result = avro_generic_record_class(schema, state); + break; + case AVRO_UNION: + result = avro_generic_union_class(schema, state); + break; + + case AVRO_LINK: + { + avro_generic_link_value_iface_t *lresult = + avro_generic_link_class(schema); + lresult->next = state->links; + state->links = lresult; + result = &lresult->parent; + break; + } + + default: + avro_set_error("Unknown schema type"); + return NULL; + } + + /* + * Add the new value implementation to the memoized state before + * we return. + */ + + avro_memoize_set(&state->mem, schema, NULL, result); + return result; +} + +avro_value_iface_t * +avro_generic_class_from_schema(avro_schema_t schema) +{ + /* + * Create a state to keep track of the value implementations + * that we create for each subschema. + */ + + memoize_state_t state; + avro_memoize_init(&state.mem); + state.links = NULL; + + /* + * Create the value implementations. + */ + + avro_generic_value_iface_t *result = + avro_generic_class_from_schema_memoized(schema, &state); + if (result == NULL) { + avro_memoize_done(&state.mem); + return NULL; + } + + /* + * Fix up any link schemas so that their value implementations + * point to their target schemas' implementations. + */ + + while (state.links != NULL) { + avro_generic_link_value_iface_t *link_iface = state.links; + avro_schema_t target_schema = + avro_schema_link_target(link_iface->schema); + + avro_generic_value_iface_t *target_iface = NULL; + if (!avro_memoize_get(&state.mem, target_schema, NULL, + (void **) &target_iface)) { + avro_set_error("Never created a value implementation for %s", + avro_schema_type_name(target_schema)); + return NULL; + } + + /* We don't keep a reference to the target + * implementation, since that would give us a reference + * cycle. */ + link_iface->target_giface = target_iface; + state.links = link_iface->next; + link_iface->next = NULL; + } + + /* + * And now we can return. + */ + + avro_memoize_done(&state.mem); + return &result->parent; +} diff --git a/fluent-bit/lib/avro/src/io.c b/fluent-bit/lib/avro/src/io.c new file mode 100644 index 000000000..c1e2f5dc9 --- /dev/null +++ b/fluent-bit/lib/avro/src/io.c @@ -0,0 +1,447 @@ +/* + * 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_private.h" +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include "dump.h" + +enum avro_io_type_t { + AVRO_FILE_IO, + AVRO_MEMORY_IO +}; +typedef enum avro_io_type_t avro_io_type_t; + +struct avro_reader_t_ { + avro_io_type_t type; + volatile int refcount; +}; + +struct avro_writer_t_ { + avro_io_type_t type; + volatile int refcount; +}; + +struct _avro_reader_file_t { + struct avro_reader_t_ reader; + FILE *fp; + int should_close; + char *cur; + char *end; + char buffer[4096]; +}; + +struct _avro_writer_file_t { + struct avro_writer_t_ writer; + FILE *fp; + int should_close; +}; + +struct _avro_reader_memory_t { + struct avro_reader_t_ reader; + const char *buf; + int64_t len; + int64_t read; +}; + +struct _avro_writer_memory_t { + struct avro_writer_t_ writer; + const char *buf; + int64_t len; + int64_t written; +}; + +#define avro_io_typeof(obj) ((obj)->type) +#define is_memory_io(obj) (obj && avro_io_typeof(obj) == AVRO_MEMORY_IO) +#define is_file_io(obj) (obj && avro_io_typeof(obj) == AVRO_FILE_IO) + +#define avro_reader_to_memory(reader_) container_of(reader_, struct _avro_reader_memory_t, reader) +#define avro_reader_to_file(reader_) container_of(reader_, struct _avro_reader_file_t, reader) +#define avro_writer_to_memory(writer_) container_of(writer_, struct _avro_writer_memory_t, writer) +#define avro_writer_to_file(writer_) container_of(writer_, struct _avro_writer_file_t, writer) + +static void reader_init(avro_reader_t reader, avro_io_type_t type) +{ + reader->type = type; + avro_refcount_set(&reader->refcount, 1); +} + +static void writer_init(avro_writer_t writer, avro_io_type_t type) +{ + writer->type = type; + avro_refcount_set(&writer->refcount, 1); +} + +avro_reader_t avro_reader_file_fp(FILE * fp, int should_close) +{ + struct _avro_reader_file_t *file_reader = + (struct _avro_reader_file_t *) avro_new(struct _avro_reader_file_t); + if (!file_reader) { + avro_set_error("Cannot allocate new file reader"); + return NULL; + } + memset(file_reader, 0, sizeof(struct _avro_reader_file_t)); + file_reader->fp = fp; + file_reader->should_close = should_close; + reader_init(&file_reader->reader, AVRO_FILE_IO); + return &file_reader->reader; +} + +avro_reader_t avro_reader_file(FILE * fp) +{ + return avro_reader_file_fp(fp, 1); +} + +avro_writer_t avro_writer_file_fp(FILE * fp, int should_close) +{ + struct _avro_writer_file_t *file_writer = + (struct _avro_writer_file_t *) avro_new(struct _avro_writer_file_t); + if (!file_writer) { + avro_set_error("Cannot allocate new file writer"); + return NULL; + } + file_writer->fp = fp; + file_writer->should_close = should_close; + writer_init(&file_writer->writer, AVRO_FILE_IO); + return &file_writer->writer; +} + +avro_writer_t avro_writer_file(FILE * fp) +{ + return avro_writer_file_fp(fp, 1); +} + +avro_reader_t avro_reader_memory(const char *buf, int64_t len) +{ + struct _avro_reader_memory_t *mem_reader = + (struct _avro_reader_memory_t *) avro_new(struct _avro_reader_memory_t); + if (!mem_reader) { + avro_set_error("Cannot allocate new memory reader"); + return NULL; + } + mem_reader->buf = buf; + mem_reader->len = len; + mem_reader->read = 0; + reader_init(&mem_reader->reader, AVRO_MEMORY_IO); + return &mem_reader->reader; +} + +void +avro_reader_memory_set_source(avro_reader_t reader, const char *buf, int64_t len) +{ + if (is_memory_io(reader)) { + struct _avro_reader_memory_t *mem_reader = avro_reader_to_memory(reader); + mem_reader->buf = buf; + mem_reader->len = len; + mem_reader->read = 0; + } +} + +avro_writer_t avro_writer_memory(const char *buf, int64_t len) +{ + struct _avro_writer_memory_t *mem_writer = + (struct _avro_writer_memory_t *) avro_new(struct _avro_writer_memory_t); + if (!mem_writer) { + avro_set_error("Cannot allocate new memory writer"); + return NULL; + } + mem_writer->buf = buf; + mem_writer->len = len; + mem_writer->written = 0; + writer_init(&mem_writer->writer, AVRO_MEMORY_IO); + return &mem_writer->writer; +} + +void +avro_writer_memory_set_dest(avro_writer_t writer, const char *buf, int64_t len) +{ + if (is_memory_io(writer)) { + struct _avro_writer_memory_t *mem_writer = avro_writer_to_memory(writer); + mem_writer->buf = buf; + mem_writer->len = len; + mem_writer->written = 0; + } +} + +static int +avro_read_memory(struct _avro_reader_memory_t *reader, void *buf, int64_t len) +{ + if (len > 0) { + if ((reader->len - reader->read) < len) { + avro_prefix_error("Cannot read %" PRIsz " bytes from memory buffer", + (size_t) len); + return ENOSPC; + } + memcpy(buf, reader->buf + reader->read, len); + reader->read += len; + } + return 0; +} + +#define bytes_available(reader) (reader->end - reader->cur) +#define buffer_reset(reader) {reader->cur = reader->end = reader->buffer;} + +static int +avro_read_file(struct _avro_reader_file_t *reader, void *buf, int64_t len) +{ + int64_t needed = len; + char *p = (char *) buf; + int rval; + + if (len == 0) { + return 0; + } + + if (needed > (int64_t) sizeof(reader->buffer)) { + if (bytes_available(reader) > 0) { + memcpy(p, reader->cur, bytes_available(reader)); + p += bytes_available(reader); + needed -= bytes_available(reader); + buffer_reset(reader); + } + rval = fread(p, 1, needed, reader->fp); + if (rval != needed) { + avro_set_error("Cannot read %" PRIsz " bytes from file", + (size_t) needed); + return EILSEQ; + } + return 0; + } else if (needed <= bytes_available(reader)) { + memcpy(p, reader->cur, needed); + reader->cur += needed; + return 0; + } else { + memcpy(p, reader->cur, bytes_available(reader)); + p += bytes_available(reader); + needed -= bytes_available(reader); + + rval = + fread(reader->buffer, 1, sizeof(reader->buffer), + reader->fp); + if (rval == 0) { + avro_set_error("Cannot read %" PRIsz " bytes from file", + (size_t) needed); + return EILSEQ; + } + reader->cur = reader->buffer; + reader->end = reader->cur + rval; + + if (bytes_available(reader) < needed) { + avro_set_error("Cannot read %" PRIsz " bytes from file", + (size_t) needed); + return EILSEQ; + } + memcpy(p, reader->cur, needed); + reader->cur += needed; + return 0; + } + avro_set_error("Cannot read %" PRIsz " bytes from file", + (size_t) needed); + return EILSEQ; +} + +int avro_read(avro_reader_t reader, void *buf, int64_t len) +{ + if (buf && len >= 0) { + if (is_memory_io(reader)) { + return avro_read_memory(avro_reader_to_memory(reader), + buf, len); + } else if (is_file_io(reader)) { + return avro_read_file(avro_reader_to_file(reader), buf, + len); + } + } + return EINVAL; +} + +static int avro_skip_memory(struct _avro_reader_memory_t *reader, int64_t len) +{ + if (len > 0) { + if ((reader->len - reader->read) < len) { + avro_set_error("Cannot skip %" PRIsz " bytes in memory buffer", + (size_t) len); + return ENOSPC; + } + reader->read += len; + } + return 0; +} + +static int avro_skip_file(struct _avro_reader_file_t *reader, int64_t len) +{ + int rval; + int64_t needed = len; + + if (len == 0) { + return 0; + } + if (needed <= bytes_available(reader)) { + reader->cur += needed; + } else { + needed -= bytes_available(reader); + buffer_reset(reader); + rval = fseek(reader->fp, needed, SEEK_CUR); + if (rval < 0) { + avro_set_error("Cannot skip %" PRIsz " bytes in file", + (size_t) len); + return rval; + } + } + return 0; +} + +int avro_skip(avro_reader_t reader, int64_t len) +{ + if (len >= 0) { + if (is_memory_io(reader)) { + return avro_skip_memory(avro_reader_to_memory(reader), + len); + } else if (is_file_io(reader)) { + return avro_skip_file(avro_reader_to_file(reader), len); + } + } + return 0; +} + +static int +avro_write_memory(struct _avro_writer_memory_t *writer, void *buf, int64_t len) +{ + if (len) { + if ((writer->len - writer->written) < len) { + avro_set_error("Cannot write %" PRIsz " bytes in memory buffer", + (size_t) len); + return ENOSPC; + } + memcpy((void *)(writer->buf + writer->written), buf, len); + writer->written += len; + } + return 0; +} + +static int +avro_write_file(struct _avro_writer_file_t *writer, void *buf, int64_t len) +{ + int rval; + if (len > 0) { + rval = fwrite(buf, len, 1, writer->fp); + if (rval == 0) { + return EIO; + } + } + return 0; +} + +int avro_write(avro_writer_t writer, void *buf, int64_t len) +{ + if (buf && len >= 0) { + if (is_memory_io(writer)) { + return avro_write_memory(avro_writer_to_memory(writer), + buf, len); + } else if (is_file_io(writer)) { + return avro_write_file(avro_writer_to_file(writer), buf, + len); + } + } + return EINVAL; +} + +void +avro_reader_reset(avro_reader_t reader) +{ + if (is_memory_io(reader)) { + avro_reader_to_memory(reader)->read = 0; + } +} + +void avro_writer_reset(avro_writer_t writer) +{ + if (is_memory_io(writer)) { + avro_writer_to_memory(writer)->written = 0; + } +} + +int64_t avro_writer_tell(avro_writer_t writer) +{ + if (is_memory_io(writer)) { + return avro_writer_to_memory(writer)->written; + } + return EINVAL; +} + +void avro_writer_flush(avro_writer_t writer) +{ + if (is_file_io(writer)) { + fflush(avro_writer_to_file(writer)->fp); + } +} + +void avro_writer_dump(avro_writer_t writer, FILE * fp) +{ + if (is_memory_io(writer)) { + dump(fp, (char *)avro_writer_to_memory(writer)->buf, + avro_writer_to_memory(writer)->written); + } +} + +void avro_reader_dump(avro_reader_t reader, FILE * fp) +{ + if (is_memory_io(reader)) { + dump(fp, (char *)avro_reader_to_memory(reader)->buf, + avro_reader_to_memory(reader)->read); + } +} + +void avro_reader_free(avro_reader_t reader) +{ + if (is_memory_io(reader)) { + avro_freet(struct _avro_reader_memory_t, reader); + } else if (is_file_io(reader)) { + if (avro_reader_to_file(reader)->should_close) { + fclose(avro_reader_to_file(reader)->fp); + } + avro_freet(struct _avro_reader_file_t, reader); + } +} + +void avro_writer_free(avro_writer_t writer) +{ + if (is_memory_io(writer)) { + avro_freet(struct _avro_writer_memory_t, writer); + } else if (is_file_io(writer)) { + if (avro_writer_to_file(writer)->should_close) { + fclose(avro_writer_to_file(writer)->fp); + } + avro_freet(struct _avro_writer_file_t, writer); + } +} + +int avro_reader_is_eof(avro_reader_t reader) +{ + if (is_file_io(reader)) { + struct _avro_reader_file_t *file = avro_reader_to_file(reader); + if (feof(file->fp)) { + return file->cur == file->end; + } + } + return 0; +} diff --git a/fluent-bit/lib/avro/src/map.c b/fluent-bit/lib/avro/src/map.c new file mode 100644 index 000000000..c85ffbd84 --- /dev/null +++ b/fluent-bit/lib/avro/src/map.c @@ -0,0 +1,130 @@ +/* + * 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 <errno.h> +#include <string.h> + +#include "avro/data.h" +#include "avro/allocation.h" +#include "avro/errors.h" +#include "st.h" + + +#define raw_entry_size(element_size) \ + (sizeof(avro_raw_map_entry_t) + element_size) + +void avro_raw_map_init(avro_raw_map_t *map, size_t element_size) +{ + memset(map, 0, sizeof(avro_raw_map_t)); + avro_raw_array_init(&map->elements, raw_entry_size(element_size)); + map->indices_by_key = st_init_strtable(); +} + + +static void +avro_raw_map_free_keys(avro_raw_map_t *map) +{ + unsigned int i; + for (i = 0; i < avro_raw_map_size(map); i++) { + void *ventry = + ((char *) map->elements.data + map->elements.element_size * i); + avro_raw_map_entry_t *entry = (avro_raw_map_entry_t *) ventry; + avro_str_free((char *) entry->key); + } +} + + +void avro_raw_map_done(avro_raw_map_t *map) +{ + avro_raw_map_free_keys(map); + avro_raw_array_done(&map->elements); + st_free_table((st_table *) map->indices_by_key); + memset(map, 0, sizeof(avro_raw_map_t)); +} + + +void avro_raw_map_clear(avro_raw_map_t *map) +{ + avro_raw_map_free_keys(map); + avro_raw_array_clear(&map->elements); + st_free_table((st_table *) map->indices_by_key); + map->indices_by_key = st_init_strtable(); +} + + +int +avro_raw_map_ensure_size(avro_raw_map_t *map, size_t desired_count) +{ + return avro_raw_array_ensure_size(&map->elements, desired_count); +} + + +void *avro_raw_map_get(const avro_raw_map_t *map, const char *key, + size_t *index) +{ + st_data_t data; + if (st_lookup((st_table *) map->indices_by_key, (st_data_t) key, &data)) { + unsigned int i = (unsigned int) data; + if (index) { + *index = i; + } + void *raw_entry = + ((char *) map->elements.data + map->elements.element_size * i); + return (char *) raw_entry + sizeof(avro_raw_map_entry_t); + } else { + return NULL; + } +} + + +int avro_raw_map_get_or_create(avro_raw_map_t *map, const char *key, + void **element, size_t *index) +{ + st_data_t data; + void *el; + unsigned int i; + int is_new; + + if (st_lookup((st_table *) map->indices_by_key, (st_data_t) key, &data)) { + i = (unsigned int) data; + void *raw_entry = + ((char *) map->elements.data + map->elements.element_size * i); + el = (char *) raw_entry + sizeof(avro_raw_map_entry_t); + is_new = 0; + } else { + i = map->elements.element_count; + avro_raw_map_entry_t *raw_entry = + (avro_raw_map_entry_t *) avro_raw_array_append(&map->elements); + raw_entry->key = avro_strdup(key); + st_insert((st_table *) map->indices_by_key, + (st_data_t) raw_entry->key, (st_data_t) i); + if (!raw_entry) { + avro_str_free((char*)raw_entry->key); + return -ENOMEM; + } + el = ((char *) raw_entry) + sizeof(avro_raw_map_entry_t); + is_new = 1; + } + + if (element) { + *element = el; + } + if (index) { + *index = i; + } + return is_new; +} diff --git a/fluent-bit/lib/avro/src/memoize.c b/fluent-bit/lib/avro/src/memoize.c new file mode 100644 index 000000000..933fecbd0 --- /dev/null +++ b/fluent-bit/lib/avro/src/memoize.c @@ -0,0 +1,165 @@ +/* + * 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 <errno.h> +#include <string.h> + +#include "avro/data.h" +#include "avro/allocation.h" +#include "avro/errors.h" +#include "avro_private.h" +#include "st.h" + + +typedef struct avro_memoize_key { + void *key1; + void *key2; +} avro_memoize_key_t; + + +static int +avro_memoize_key_cmp(avro_memoize_key_t *a, avro_memoize_key_t *b) +{ + /* + * This isn't a proper cmp operation, since it always returns 1 + * if the keys are different. But that's okay for the hash + * table implementation we're using. + */ + + return (a->key1 != b->key1) || (a->key2 != b->key2); +} + + +static int +avro_memoize_key_hash(avro_memoize_key_t *a) +{ + return ((uintptr_t) a->key1) ^ ((uintptr_t) a->key2); +} + + +static struct st_hash_type avro_memoize_hash_type = { + HASH_FUNCTION_CAST avro_memoize_key_cmp, + HASH_FUNCTION_CAST avro_memoize_key_hash +}; + + +void +avro_memoize_init(avro_memoize_t *mem) +{ + memset(mem, 0, sizeof(avro_memoize_t)); + mem->cache = st_init_table(&avro_memoize_hash_type); +} + + +static int +avro_memoize_free_key(avro_memoize_key_t *key, void *result, void *dummy) +{ + AVRO_UNUSED(result); + AVRO_UNUSED(dummy); + avro_freet(avro_memoize_key_t, key); + return ST_CONTINUE; +} + + +void +avro_memoize_done(avro_memoize_t *mem) +{ + st_foreach((st_table *) mem->cache, HASH_FUNCTION_CAST avro_memoize_free_key, 0); + st_free_table((st_table *) mem->cache); + memset(mem, 0, sizeof(avro_memoize_t)); +} + + +int +avro_memoize_get(avro_memoize_t *mem, + void *key1, void *key2, + void **result) +{ + avro_memoize_key_t key; + key.key1 = key1; + key.key2 = key2; + + union { + st_data_t data; + void *value; + } val; + + if (st_lookup((st_table *) mem->cache, (st_data_t) &key, &val.data)) { + if (result) { + *result = val.value; + } + return 1; + } else { + return 0; + } +} + + +void +avro_memoize_set(avro_memoize_t *mem, + void *key1, void *key2, + void *result) +{ + /* + * First see if there's already a cached value for this key. If + * so, we don't want to allocate a new avro_memoize_key_t + * instance. + */ + + avro_memoize_key_t key; + key.key1 = key1; + key.key2 = key2; + + union { + st_data_t data; + void *value; + } val; + + if (st_lookup((st_table *) mem->cache, (st_data_t) &key, &val.data)) { + st_insert((st_table *) mem->cache, (st_data_t) &key, (st_data_t) result); + return; + } + + /* + * If it's a new key pair, then we do need to allocate. + */ + + avro_memoize_key_t *real_key = (avro_memoize_key_t *) avro_new(avro_memoize_key_t); + real_key->key1 = key1; + real_key->key2 = key2; + + st_insert((st_table *) mem->cache, (st_data_t) real_key, (st_data_t) result); +} + + +void +avro_memoize_delete(avro_memoize_t *mem, void *key1, void *key2) +{ + avro_memoize_key_t key; + key.key1 = key1; + key.key2 = key2; + + union { + st_data_t data; + avro_memoize_key_t *key; + } real_key; + + real_key.key = &key; + if (st_delete((st_table *) mem->cache, &real_key.data, NULL)) { + avro_freet(avro_memoize_key_t, real_key.key); + } +} diff --git a/fluent-bit/lib/avro/src/resolved-reader.c b/fluent-bit/lib/avro/src/resolved-reader.c new file mode 100644 index 000000000..30d7b8c97 --- /dev/null +++ b/fluent-bit/lib/avro/src/resolved-reader.c @@ -0,0 +1,3377 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro_private.h" +#include "avro/allocation.h" +#include "avro/basics.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro/refcount.h" +#include "avro/resolver.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "st.h" + +#ifndef AVRO_RESOLVER_DEBUG +#define AVRO_RESOLVER_DEBUG 0 +#endif + +#if AVRO_RESOLVER_DEBUG +#include <stdio.h> +#define DEBUG(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (0) +#else +#define DEBUG(...) /* don't print messages */ +#endif + + +typedef struct avro_resolved_reader avro_resolved_reader_t; + +struct avro_resolved_reader { + avro_value_iface_t parent; + + /** The reference count for this interface. */ + volatile int refcount; + + /** The writer schema. */ + avro_schema_t wschema; + + /** The reader schema. */ + avro_schema_t rschema; + + /* The size of the value instances for this resolver. */ + size_t instance_size; + + /* A function to calculate the instance size once the overall + * top-level resolver (and all of its children) have been + * constructed. */ + void + (*calculate_size)(avro_resolved_reader_t *iface); + + /* A free function for this resolver */ + void + (*free_iface)(avro_resolved_reader_t *iface, st_table *freeing); + + /* An initialization function for instances of this resolver. */ + int + (*init)(const avro_resolved_reader_t *iface, void *self); + + /* A finalization function for instances of this resolver. */ + void + (*done)(const avro_resolved_reader_t *iface, void *self); + + /* Clear out any existing wrappers, if any */ + int + (*reset_wrappers)(const avro_resolved_reader_t *iface, void *self); +}; + +#define avro_resolved_reader_calculate_size(iface) \ + do { \ + if ((iface)->calculate_size != NULL) { \ + (iface)->calculate_size((iface)); \ + } \ + } while (0) +#define avro_resolved_reader_init(iface, self) \ + ((iface)->init == NULL? 0: (iface)->init((iface), (self))) +#define avro_resolved_reader_done(iface, self) \ + ((iface)->done == NULL? (void) 0: (iface)->done((iface), (self))) +#define avro_resolved_reader_reset_wrappers(iface, self) \ + ((iface)->reset_wrappers == NULL? 0: \ + (iface)->reset_wrappers((iface), (self))) + + +/* + * We assume that each instance type in this value contains an an + * avro_value_t as its first element, which is the current wrapped + * value. + */ + +void +avro_resolved_reader_set_source(avro_value_t *resolved, + avro_value_t *dest) +{ + avro_value_t *self = (avro_value_t *) resolved->self; + if (self->self != NULL) { + avro_value_decref(self); + } + avro_value_copy_ref(self, dest); +} + +void +avro_resolved_reader_clear_source(avro_value_t *resolved) +{ + avro_value_t *self = (avro_value_t *) resolved->self; + if (self->self != NULL) { + avro_value_decref(self); + } + self->iface = NULL; + self->self = NULL; +} + +int +avro_resolved_reader_new_value(avro_value_iface_t *viface, + avro_value_t *value) +{ + int rval; + avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + void *self = avro_malloc(iface->instance_size + sizeof(volatile int)); + if (self == NULL) { + value->iface = NULL; + value->self = NULL; + return ENOMEM; + } + + memset(self, 0, iface->instance_size + sizeof(volatile int)); + volatile int *refcount = (volatile int *) self; + self = (char *) self + sizeof(volatile int); + + rval = avro_resolved_reader_init(iface, self); + if (rval != 0) { + avro_free(self, iface->instance_size + sizeof(volatile int)); + value->iface = NULL; + value->self = NULL; + return rval; + } + + *refcount = 1; + value->iface = avro_value_iface_incref(viface); + value->self = self; + return 0; +} + +static void +avro_resolved_reader_free_value(const avro_value_iface_t *viface, void *vself) +{ + avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + avro_value_t *self = (avro_value_t *) vself; + + avro_resolved_reader_done(iface, vself); + if (self->self != NULL) { + avro_value_decref(self); + } + + vself = (char *) vself - sizeof(volatile int); + avro_free(vself, iface->instance_size + sizeof(volatile int)); +} + +static void +avro_resolved_reader_incref(avro_value_t *value) +{ + /* + * This only works if you pass in the top-level value. + */ + + volatile int *refcount = (volatile int *) ((char *) value->self - sizeof(volatile int)); + avro_refcount_inc(refcount); +} + +static void +avro_resolved_reader_decref(avro_value_t *value) +{ + /* + * This only works if you pass in the top-level value. + */ + + volatile int *refcount = (volatile int *) ((char *) value->self - sizeof(volatile int)); + if (avro_refcount_dec(refcount)) { + avro_resolved_reader_free_value(value->iface, value->self); + } +} + + +static avro_value_iface_t * +avro_resolved_reader_incref_iface(avro_value_iface_t *viface) +{ + avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +free_resolver(avro_resolved_reader_t *iface, st_table *freeing) +{ + /* First check if we've already started freeing this resolver. */ + if (st_lookup(freeing, (st_data_t) iface, NULL)) { + DEBUG("Already freed %p", iface); + return; + } + + /* Otherwise add this resolver to the freeing set, then free it. */ + st_insert(freeing, (st_data_t) iface, (st_data_t) NULL); + DEBUG("Freeing resolver %p (%s->%s)", iface, + avro_schema_type_name(iface->wschema), + avro_schema_type_name(iface->rschema)); + + iface->free_iface(iface, freeing); +} + +static void +avro_resolved_reader_calculate_size_(avro_resolved_reader_t *iface) +{ + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_value_t); +} + +static void +avro_resolved_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + AVRO_UNUSED(freeing); + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_reader_t, iface); +} + +static void +avro_resolved_reader_decref_iface(avro_value_iface_t *viface) +{ + avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + DEBUG("Decref resolver %p (before=%d)", iface, iface->refcount); + if (avro_refcount_dec(&iface->refcount)) { + st_table *freeing = st_init_numtable(); + free_resolver(iface, freeing); + st_free_table(freeing); + } +} + +static int +avro_resolved_reader_reset(const avro_value_iface_t *viface, void *vself) +{ + /* + * To reset a wrapped value, we first clear out any wrappers, + * and then have the wrapped value reset itself. + */ + + int rval; + avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + avro_value_t *self = (avro_value_t *) vself; + check(rval, avro_resolved_reader_reset_wrappers(iface, vself)); + return avro_value_reset(self); +} + +static avro_type_t +avro_resolved_reader_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(vself); + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + return avro_typeof(iface->rschema); +} + +static avro_schema_t +avro_resolved_reader_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(vself); + avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + return iface->rschema; +} + + +static avro_resolved_reader_t * +avro_resolved_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_reader_t); + memset(self, 0, sizeof(avro_resolved_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_reader_get_type; + self->parent.get_schema = avro_resolved_reader_get_schema; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->calculate_size = avro_resolved_reader_calculate_size_; + self->free_iface = avro_resolved_reader_free_iface; + self->reset_wrappers = NULL; + return self; +} + + +/*----------------------------------------------------------------------- + * Memoized resolvers + */ + +typedef struct avro_resolved_link_reader avro_resolved_link_reader_t; + +typedef struct memoize_state_t { + avro_memoize_t mem; + avro_resolved_link_reader_t *links; +} memoize_state_t; + +static avro_resolved_reader_t * +avro_resolved_reader_new_memoized(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema); + + +/*----------------------------------------------------------------------- + * Recursive schemas + */ + +/* + * Recursive schemas are handled specially; the value implementation for + * an AVRO_LINK schema is simply a wrapper around the value + * implementation for the link's target schema. The value methods all + * delegate to the wrapped implementation. + * + * Complicating the links here is that we might be linking to the writer + * schema or the reader schema. This only matters for a couple of + * methods, so instead of keeping a boolean flag in the value interface, + * we just have separate method implementations that we slot in + * appropriately. + */ + +struct avro_resolved_link_reader { + avro_resolved_reader_t parent; + + /** + * A pointer to the “next” link resolver that we've had to + * create. We use this as we're creating the overall top-level + * resolver to keep track of which ones we have to fix up + * afterwards. + */ + avro_resolved_link_reader_t *next; + + /** The target's implementation. */ + avro_resolved_reader_t *target_resolver; +}; + +typedef struct avro_resolved_link_value { + avro_value_t wrapped; + avro_value_t target; +} avro_resolved_link_value_t; + +static void +avro_resolved_wlink_reader_calculate_size(avro_resolved_reader_t *iface) +{ + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for [%s]->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_link_value_t); +} + +static void +avro_resolved_rlink_reader_calculate_size(avro_resolved_reader_t *iface) +{ + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->[%s]", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_link_value_t); +} + +static void +avro_resolved_link_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + avro_resolved_link_reader_t *liface = + container_of(iface, avro_resolved_link_reader_t, parent); + if (liface->target_resolver != NULL) { + free_resolver(liface->target_resolver, freeing); + } + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_link_reader_t, iface); +} + +static int +avro_resolved_link_reader_init(const avro_resolved_reader_t *iface, void *vself) +{ + int rval; + const avro_resolved_link_reader_t *liface = + container_of(iface, avro_resolved_link_reader_t, parent); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + size_t target_instance_size = liface->target_resolver->instance_size; + + self->target.iface = &liface->target_resolver->parent; + self->target.self = avro_malloc(target_instance_size); + if (self->target.self == NULL) { + return ENOMEM; + } + DEBUG("Allocated <%p:%" PRIsz "> for link", self->target.self, target_instance_size); + + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + + rval = avro_resolved_reader_init(liface->target_resolver, self->target.self); + if (rval != 0) { + avro_free(self->target.self, target_instance_size); + } + return rval; +} + +static void +avro_resolved_link_reader_done(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_link_reader_t *liface = + container_of(iface, avro_resolved_link_reader_t, parent); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + size_t target_instance_size = liface->target_resolver->instance_size; + DEBUG("Freeing <%p:%" PRIsz "> for link", self->target.self, target_instance_size); + avro_resolved_reader_done(liface->target_resolver, self->target.self); + avro_free(self->target.self, target_instance_size); + self->target.iface = NULL; + self->target.self = NULL; +} + +static int +avro_resolved_link_reader_reset(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_link_reader_t *liface = + container_of(iface, avro_resolved_link_reader_t, parent); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + return avro_resolved_reader_reset_wrappers + (liface->target_resolver, self->target.self); +} + +static avro_type_t +avro_resolved_link_reader_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_type(&self->target); +} + +static avro_schema_t +avro_resolved_link_reader_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_schema(&self->target); +} + +static int +avro_resolved_link_reader_get_boolean(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_boolean(&self->target, out); +} + +static int +avro_resolved_link_reader_get_bytes(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_bytes(&self->target, buf, size); +} + +static int +avro_resolved_link_reader_grab_bytes(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_grab_bytes(&self->target, dest); +} + +static int +avro_resolved_link_reader_get_double(const avro_value_iface_t *iface, + const void *vself, double *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_double(&self->target, out); +} + +static int +avro_resolved_link_reader_get_float(const avro_value_iface_t *iface, + const void *vself, float *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_float(&self->target, out); +} + +static int +avro_resolved_link_reader_get_int(const avro_value_iface_t *iface, + const void *vself, int32_t *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_int(&self->target, out); +} + +static int +avro_resolved_link_reader_get_long(const avro_value_iface_t *iface, + const void *vself, int64_t *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_long(&self->target, out); +} + +static int +avro_resolved_link_reader_get_null(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_null(&self->target); +} + +static int +avro_resolved_link_reader_get_string(const avro_value_iface_t *iface, + const void *vself, const char **str, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_string(&self->target, str, size); +} + +static int +avro_resolved_link_reader_grab_string(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_grab_string(&self->target, dest); +} + +static int +avro_resolved_link_reader_get_enum(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_enum(&self->target, out); +} + +static int +avro_resolved_link_reader_get_fixed(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_fixed(&self->target, buf, size); +} + +static int +avro_resolved_link_reader_grab_fixed(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_grab_fixed(&self->target, dest); +} + +static int +avro_resolved_link_reader_set_boolean(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_boolean(&self->target, val); +} + +static int +avro_resolved_link_reader_set_bytes(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_bytes(&self->target, buf, size); +} + +static int +avro_resolved_link_reader_give_bytes(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_give_bytes(&self->target, buf); +} + +static int +avro_resolved_link_reader_set_double(const avro_value_iface_t *iface, + void *vself, double val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_double(&self->target, val); +} + +static int +avro_resolved_link_reader_set_float(const avro_value_iface_t *iface, + void *vself, float val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_float(&self->target, val); +} + +static int +avro_resolved_link_reader_set_int(const avro_value_iface_t *iface, + void *vself, int32_t val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_int(&self->target, val); +} + +static int +avro_resolved_link_reader_set_long(const avro_value_iface_t *iface, + void *vself, int64_t val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_long(&self->target, val); +} + +static int +avro_resolved_link_reader_set_null(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_null(&self->target); +} + +static int +avro_resolved_link_reader_set_string(const avro_value_iface_t *iface, + void *vself, const char *str) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_string(&self->target, str); +} + +static int +avro_resolved_link_reader_set_string_len(const avro_value_iface_t *iface, + void *vself, const char *str, size_t size) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_string_len(&self->target, str, size); +} + +static int +avro_resolved_link_reader_give_string_len(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_give_string_len(&self->target, buf); +} + +static int +avro_resolved_link_reader_set_enum(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_enum(&self->target, val); +} + +static int +avro_resolved_link_reader_set_fixed(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_fixed(&self->target, buf, size); +} + +static int +avro_resolved_link_reader_give_fixed(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_give_fixed(&self->target, buf); +} + +static int +avro_resolved_link_reader_get_size(const avro_value_iface_t *iface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_size(&self->target, size); +} + +static int +avro_resolved_link_reader_get_by_index(const avro_value_iface_t *iface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_by_index(&self->target, index, child, name); +} + +static int +avro_resolved_link_reader_get_by_name(const avro_value_iface_t *iface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_by_name(&self->target, name, child, index); +} + +static int +avro_resolved_link_reader_get_discriminant(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_discriminant(&self->target, out); +} + +static int +avro_resolved_link_reader_get_current_branch(const avro_value_iface_t *iface, + const void *vself, avro_value_t *branch) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_current_branch(&self->target, branch); +} + +static int +avro_resolved_link_reader_append(const avro_value_iface_t *iface, + void *vself, avro_value_t *child_out, + size_t *new_index) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_append(&self->target, child_out, new_index); +} + +static int +avro_resolved_link_reader_add(const avro_value_iface_t *iface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_add(&self->target, key, child, index, is_new); +} + +static int +avro_resolved_link_reader_set_branch(const avro_value_iface_t *iface, + void *vself, int discriminant, + avro_value_t *branch) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_branch(&self->target, discriminant, branch); +} + +static avro_resolved_link_reader_t * +avro_resolved_link_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_link_reader_t); + memset(self, 0, sizeof(avro_resolved_link_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_link_reader_get_type; + self->parent.get_schema = avro_resolved_link_reader_get_schema; + self->parent.get_size = avro_resolved_link_reader_get_size; + self->parent.get_by_index = avro_resolved_link_reader_get_by_index; + self->parent.get_by_name = avro_resolved_link_reader_get_by_name; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->free_iface = avro_resolved_link_reader_free_iface; + self->init = avro_resolved_link_reader_init; + self->done = avro_resolved_link_reader_done; + self->reset_wrappers = avro_resolved_link_reader_reset; + + self->parent.get_boolean = avro_resolved_link_reader_get_boolean; + self->parent.get_bytes = avro_resolved_link_reader_get_bytes; + self->parent.grab_bytes = avro_resolved_link_reader_grab_bytes; + self->parent.get_double = avro_resolved_link_reader_get_double; + self->parent.get_float = avro_resolved_link_reader_get_float; + self->parent.get_int = avro_resolved_link_reader_get_int; + self->parent.get_long = avro_resolved_link_reader_get_long; + self->parent.get_null = avro_resolved_link_reader_get_null; + self->parent.get_string = avro_resolved_link_reader_get_string; + self->parent.grab_string = avro_resolved_link_reader_grab_string; + self->parent.get_enum = avro_resolved_link_reader_get_enum; + self->parent.get_fixed = avro_resolved_link_reader_get_fixed; + self->parent.grab_fixed = avro_resolved_link_reader_grab_fixed; + + self->parent.set_boolean = avro_resolved_link_reader_set_boolean; + self->parent.set_bytes = avro_resolved_link_reader_set_bytes; + self->parent.give_bytes = avro_resolved_link_reader_give_bytes; + self->parent.set_double = avro_resolved_link_reader_set_double; + self->parent.set_float = avro_resolved_link_reader_set_float; + self->parent.set_int = avro_resolved_link_reader_set_int; + self->parent.set_long = avro_resolved_link_reader_set_long; + self->parent.set_null = avro_resolved_link_reader_set_null; + self->parent.set_string = avro_resolved_link_reader_set_string; + self->parent.set_string_len = avro_resolved_link_reader_set_string_len; + self->parent.give_string_len = avro_resolved_link_reader_give_string_len; + self->parent.set_enum = avro_resolved_link_reader_set_enum; + self->parent.set_fixed = avro_resolved_link_reader_set_fixed; + self->parent.give_fixed = avro_resolved_link_reader_give_fixed; + + self->parent.get_size = avro_resolved_link_reader_get_size; + self->parent.get_by_index = avro_resolved_link_reader_get_by_index; + self->parent.get_by_name = avro_resolved_link_reader_get_by_name; + self->parent.get_discriminant = avro_resolved_link_reader_get_discriminant; + self->parent.get_current_branch = avro_resolved_link_reader_get_current_branch; + + self->parent.append = avro_resolved_link_reader_append; + self->parent.add = avro_resolved_link_reader_add; + self->parent.set_branch = avro_resolved_link_reader_set_branch; + + return container_of(self, avro_resolved_link_reader_t, parent); +} + +static avro_resolved_reader_t * +try_wlink(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + AVRO_UNUSED(rschema); + + /* + * For link schemas, we create a special value implementation + * that allocates space for its wrapped value at runtime. This + * lets us handle recursive types without having to instantiate + * in infinite-size value. + */ + + avro_schema_t wtarget = avro_schema_link_target(wschema); + avro_resolved_link_reader_t *lself = + avro_resolved_link_reader_create(wtarget, rschema); + avro_memoize_set(&state->mem, wschema, rschema, lself); + + avro_resolved_reader_t *target_resolver = + avro_resolved_reader_new_memoized(state, wtarget, rschema); + if (target_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&lself->parent.parent); + avro_prefix_error("Link target isn't compatible: "); + DEBUG("%s", avro_strerror()); + return NULL; + } + + lself->parent.calculate_size = avro_resolved_wlink_reader_calculate_size; + lself->target_resolver = target_resolver; + lself->next = state->links; + state->links = lself; + + return &lself->parent; +} + +static avro_resolved_reader_t * +try_rlink(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + AVRO_UNUSED(rschema); + + /* + * For link schemas, we create a special value implementation + * that allocates space for its wrapped value at runtime. This + * lets us handle recursive types without having to instantiate + * in infinite-size value. + */ + + avro_schema_t rtarget = avro_schema_link_target(rschema); + avro_resolved_link_reader_t *lself = + avro_resolved_link_reader_create(wschema, rtarget); + avro_memoize_set(&state->mem, wschema, rschema, lself); + + avro_resolved_reader_t *target_resolver = + avro_resolved_reader_new_memoized(state, wschema, rtarget); + if (target_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&lself->parent.parent); + avro_prefix_error("Link target isn't compatible: "); + DEBUG("%s", avro_strerror()); + return NULL; + } + + lself->parent.calculate_size = avro_resolved_rlink_reader_calculate_size; + lself->target_resolver = target_resolver; + lself->next = state->links; + state->links = lself; + + return &lself->parent; +} + + +/*----------------------------------------------------------------------- + * boolean + */ + +static int +avro_resolved_reader_get_boolean(const avro_value_iface_t *viface, + const void *vself, int *val) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting boolean from %p", src->self); + return avro_value_get_boolean(src, val); +} + +static avro_resolved_reader_t * +try_boolean(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_boolean(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_boolean = avro_resolved_reader_get_boolean; + return self; + } + avro_set_error("Writer %s not compatible with reader boolean", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * bytes + */ + +static int +avro_resolved_reader_get_bytes(const avro_value_iface_t *viface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting bytes from %p", src->self); + return avro_value_get_bytes(src, buf, size); +} + +static int +avro_resolved_reader_grab_bytes(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Grabbing bytes from %p", src->self); + return avro_value_grab_bytes(src, dest); +} + +static avro_resolved_reader_t * +try_bytes(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_bytes(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_bytes = avro_resolved_reader_get_bytes; + self->parent.grab_bytes = avro_resolved_reader_grab_bytes; + return self; + } + avro_set_error("Writer %s not compatible with reader bytes", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * double + */ + +static int +avro_resolved_reader_get_double(const avro_value_iface_t *viface, + const void *vself, double *val) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting double from %p", src->self); + return avro_value_get_double(src, val); +} + +static int +avro_resolved_reader_get_double_float(const avro_value_iface_t *viface, + const void *vself, double *val) +{ + int rval; + float real_val; + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Promoting double from float %p", src->self); + check(rval, avro_value_get_float(src, &real_val)); + *val = real_val; + return 0; +} + +static int +avro_resolved_reader_get_double_int(const avro_value_iface_t *viface, + const void *vself, double *val) +{ + int rval; + int32_t real_val; + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Promoting double from int %p", src->self); + check(rval, avro_value_get_int(src, &real_val)); + *val = real_val; + return 0; +} + +static int +avro_resolved_reader_get_double_long(const avro_value_iface_t *viface, + const void *vself, double *val) +{ + int rval; + int64_t real_val; + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Promoting double from long %p", src->self); + check(rval, avro_value_get_long(src, &real_val)); + *val = (double) real_val; + return 0; +} + +static avro_resolved_reader_t * +try_double(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_double(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_double = avro_resolved_reader_get_double; + return self; + } + + else if (is_avro_float(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_double = avro_resolved_reader_get_double_float; + return self; + } + + else if (is_avro_int32(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_double = avro_resolved_reader_get_double_int; + return self; + } + + else if (is_avro_int64(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_double = avro_resolved_reader_get_double_long; + return self; + } + + avro_set_error("Writer %s not compatible with reader double", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * float + */ + +static int +avro_resolved_reader_get_float(const avro_value_iface_t *viface, + const void *vself, float *val) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting float from %p", src->self); + return avro_value_get_float(src, val); +} + +static int +avro_resolved_reader_get_float_int(const avro_value_iface_t *viface, + const void *vself, float *val) +{ + int rval; + int32_t real_val; + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Promoting float from int %p", src->self); + check(rval, avro_value_get_int(src, &real_val)); + *val = (float) real_val; + return 0; +} + +static int +avro_resolved_reader_get_float_long(const avro_value_iface_t *viface, + const void *vself, float *val) +{ + int rval; + int64_t real_val; + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Promoting float from long %p", src->self); + check(rval, avro_value_get_long(src, &real_val)); + *val = (float) real_val; + return 0; +} + +static avro_resolved_reader_t * +try_float(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_float(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_float = avro_resolved_reader_get_float; + return self; + } + + else if (is_avro_int32(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_float = avro_resolved_reader_get_float_int; + return self; + } + + else if (is_avro_int64(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_float = avro_resolved_reader_get_float_long; + return self; + } + + avro_set_error("Writer %s not compatible with reader float", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * int + */ + +static int +avro_resolved_reader_get_int(const avro_value_iface_t *viface, + const void *vself, int32_t *val) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting int from %p", src->self); + return avro_value_get_int(src, val); +} + +static avro_resolved_reader_t * +try_int(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_int32(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_int = avro_resolved_reader_get_int; + return self; + } + avro_set_error("Writer %s not compatible with reader int", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * long + */ + +static int +avro_resolved_reader_get_long(const avro_value_iface_t *viface, + const void *vself, int64_t *val) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting long from %p", src->self); + return avro_value_get_long(src, val); +} + +static int +avro_resolved_reader_get_long_int(const avro_value_iface_t *viface, + const void *vself, int64_t *val) +{ + int rval; + int32_t real_val; + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Promoting long from int %p", src->self); + check(rval, avro_value_get_int(src, &real_val)); + *val = real_val; + return 0; +} + +static avro_resolved_reader_t * +try_long(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_int64(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_long = avro_resolved_reader_get_long; + return self; + } + + else if (is_avro_int32(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_long = avro_resolved_reader_get_long_int; + return self; + } + + avro_set_error("Writer %s not compatible with reader long", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * null + */ + +static int +avro_resolved_reader_get_null(const avro_value_iface_t *viface, + const void *vself) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting null from %p", src->self); + return avro_value_get_null(src); +} + +static avro_resolved_reader_t * +try_null(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_null(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_null = avro_resolved_reader_get_null; + return self; + } + avro_set_error("Writer %s not compatible with reader null", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * string + */ + +static int +avro_resolved_reader_get_string(const avro_value_iface_t *viface, + const void *vself, const char **str, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting string from %p", src->self); + return avro_value_get_string(src, str, size); +} + +static int +avro_resolved_reader_grab_string(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Grabbing string from %p", src->self); + return avro_value_grab_string(src, dest); +} + +static avro_resolved_reader_t * +try_string(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + if (is_avro_string(wschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_string = avro_resolved_reader_get_string; + self->parent.grab_string = avro_resolved_reader_grab_string; + return self; + } + avro_set_error("Writer %s not compatible with reader string", + avro_schema_type_name(wschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * array + */ + +typedef struct avro_resolved_array_reader { + avro_resolved_reader_t parent; + avro_resolved_reader_t *child_resolver; +} avro_resolved_array_reader_t; + +typedef struct avro_resolved_array_value { + avro_value_t wrapped; + avro_raw_array_t children; +} avro_resolved_array_value_t; + +static void +avro_resolved_array_reader_calculate_size(avro_resolved_reader_t *iface) +{ + avro_resolved_array_reader_t *aiface = + container_of(iface, avro_resolved_array_reader_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_array_value_t); + + avro_resolved_reader_calculate_size(aiface->child_resolver); +} + +static void +avro_resolved_array_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + avro_resolved_array_reader_t *aiface = + container_of(iface, avro_resolved_array_reader_t, parent); + free_resolver(aiface->child_resolver, freeing); + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_array_reader_t, iface); +} + +static int +avro_resolved_array_reader_init(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_array_reader_t *aiface = + container_of(iface, avro_resolved_array_reader_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + size_t child_instance_size = aiface->child_resolver->instance_size; + DEBUG("Initializing child array (child_size=%" PRIsz ")", child_instance_size); + avro_raw_array_init(&self->children, child_instance_size); + return 0; +} + +static void +avro_resolved_array_reader_free_elements(const avro_resolved_reader_t *child_iface, + avro_resolved_array_value_t *self) +{ + size_t i; + for (i = 0; i < avro_raw_array_size(&self->children); i++) { + void *child_self = avro_raw_array_get_raw(&self->children, i); + avro_resolved_reader_done(child_iface, child_self); + } +} + +static void +avro_resolved_array_reader_done(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_array_reader_t *aiface = + container_of(iface, avro_resolved_array_reader_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + avro_resolved_array_reader_free_elements(aiface->child_resolver, self); + avro_raw_array_done(&self->children); +} + +static int +avro_resolved_array_reader_reset(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_array_reader_t *aiface = + container_of(iface, avro_resolved_array_reader_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + + /* Clear out our cache of wrapped children */ + avro_resolved_array_reader_free_elements(aiface->child_resolver, self); + avro_raw_array_clear(&self->children); + return 0; +} + +static int +avro_resolved_array_reader_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_resolved_array_value_t *self = (const avro_resolved_array_value_t *) vself; + return avro_value_get_size(&self->wrapped, size); +} + +static int +avro_resolved_array_reader_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + int rval; + size_t old_size; + size_t new_size; + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_array_reader_t *aiface = + container_of(iface, avro_resolved_array_reader_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + + /* + * Ensure that our child wrapper array is big enough to hold + * this many elements. + */ + new_size = index + 1; + check(rval, avro_raw_array_ensure_size0(&self->children, new_size)); + old_size = avro_raw_array_size(&self->children); + if (old_size <= index) { + size_t i; + for (i = old_size; i < new_size; i++) { + check(rval, avro_resolved_reader_init + (aiface->child_resolver, + avro_raw_array_get_raw(&self->children, i))); + } + avro_raw_array_size(&self->children) = index+1; + } + + child->iface = &aiface->child_resolver->parent; + child->self = avro_raw_array_get_raw(&self->children, index); + + DEBUG("Getting element %" PRIsz " from array %p", index, self->wrapped.self); + return avro_value_get_by_index(&self->wrapped, index, (avro_value_t *) child->self, name); +} + +static avro_resolved_array_reader_t * +avro_resolved_array_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_array_reader_t); + memset(self, 0, sizeof(avro_resolved_array_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_reader_get_type; + self->parent.get_schema = avro_resolved_reader_get_schema; + self->parent.get_size = avro_resolved_array_reader_get_size; + self->parent.get_by_index = avro_resolved_array_reader_get_by_index; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->calculate_size = avro_resolved_array_reader_calculate_size; + self->free_iface = avro_resolved_array_reader_free_iface; + self->init = avro_resolved_array_reader_init; + self->done = avro_resolved_array_reader_done; + self->reset_wrappers = avro_resolved_array_reader_reset; + return container_of(self, avro_resolved_array_reader_t, parent); +} + +static avro_resolved_reader_t * +try_array(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * First verify that the writer is an array. + */ + + if (!is_avro_array(wschema)) { + return 0; + } + + /* + * Array schemas have to have compatible element schemas to be + * compatible themselves. Try to create an resolver to check + * the compatibility. + */ + + avro_resolved_array_reader_t *aself = + avro_resolved_array_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, aself); + + avro_schema_t witems = avro_schema_array_items(wschema); + avro_schema_t ritems = avro_schema_array_items(rschema); + + avro_resolved_reader_t *item_resolver = + avro_resolved_reader_new_memoized(state, witems, ritems); + if (item_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&aself->parent.parent); + avro_prefix_error("Array values aren't compatible: "); + return NULL; + } + + /* + * The two schemas are compatible. Store the item schema's + * resolver into the child_resolver field. + */ + + aself->child_resolver = item_resolver; + return &aself->parent; +} + + +/*----------------------------------------------------------------------- + * enum + */ + +static int +avro_resolved_reader_get_enum(const avro_value_iface_t *viface, + const void *vself, int *val) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting enum from %p", src->self); + return avro_value_get_enum(src, val); +} + +static avro_resolved_reader_t * +try_enum(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * Enum schemas have to have the same name — but not the same + * list of symbols — to be compatible. + */ + + if (is_avro_enum(wschema)) { + const char *wname = avro_schema_name(wschema); + const char *rname = avro_schema_name(rschema); + + if (strcmp(wname, rname) == 0) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_enum = avro_resolved_reader_get_enum; + return self; + } + } + avro_set_error("Writer %s not compatible with reader %s", + avro_schema_type_name(wschema), + avro_schema_type_name(rschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * fixed + */ + +static int +avro_resolved_reader_get_fixed(const avro_value_iface_t *viface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Getting fixed from %p", vself); + return avro_value_get_fixed(src, buf, size); +} + +static int +avro_resolved_reader_grab_fixed(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + DEBUG("Grabbing fixed from %p", vself); + return avro_value_grab_fixed(src, dest); +} + +static avro_resolved_reader_t * +try_fixed(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * Fixed schemas need the same name and size to be compatible. + */ + + if (avro_schema_equal(wschema, rschema)) { + avro_resolved_reader_t *self = + avro_resolved_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, self); + self->parent.get_fixed = avro_resolved_reader_get_fixed; + self->parent.grab_fixed = avro_resolved_reader_grab_fixed; + return self; + } + avro_set_error("Writer %s not compatible with reader %s", + avro_schema_type_name(wschema), + avro_schema_type_name(rschema)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * map + */ + +typedef struct avro_resolved_map_reader { + avro_resolved_reader_t parent; + avro_resolved_reader_t *child_resolver; +} avro_resolved_map_reader_t; + +typedef struct avro_resolved_map_value { + avro_value_t wrapped; + avro_raw_array_t children; +} avro_resolved_map_value_t; + +static void +avro_resolved_map_reader_calculate_size(avro_resolved_reader_t *iface) +{ + avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_map_value_t); + + avro_resolved_reader_calculate_size(miface->child_resolver); +} + +static void +avro_resolved_map_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + free_resolver(miface->child_resolver, freeing); + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_map_reader_t, iface); +} + +static int +avro_resolved_map_reader_init(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + size_t child_instance_size = miface->child_resolver->instance_size; + DEBUG("Initializing child array for map (child_size=%" PRIsz ")", child_instance_size); + avro_raw_array_init(&self->children, child_instance_size); + return 0; +} + +static void +avro_resolved_map_reader_free_elements(const avro_resolved_reader_t *child_iface, + avro_resolved_map_value_t *self) +{ + size_t i; + for (i = 0; i < avro_raw_array_size(&self->children); i++) { + void *child_self = avro_raw_array_get_raw(&self->children, i); + avro_resolved_reader_done(child_iface, child_self); + } +} + +static void +avro_resolved_map_reader_done(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + avro_resolved_map_reader_free_elements(miface->child_resolver, self); + avro_raw_array_done(&self->children); +} + +static int +avro_resolved_map_reader_reset(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + + /* Clear out our cache of wrapped children */ + avro_resolved_map_reader_free_elements(miface->child_resolver, self); + return 0; +} + +static int +avro_resolved_map_reader_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(viface); + const avro_value_t *src = (const avro_value_t *) vself; + return avro_value_get_size(src, size); +} + +static int +avro_resolved_map_reader_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + int rval; + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + + /* + * Ensure that our child wrapper array is big enough to hold + * this many elements. + */ + check(rval, avro_raw_array_ensure_size0(&self->children, index+1)); + if (avro_raw_array_size(&self->children) <= index) { + avro_raw_array_size(&self->children) = index+1; + } + + child->iface = &miface->child_resolver->parent; + child->self = avro_raw_array_get_raw(&self->children, index); + + DEBUG("Getting element %" PRIsz " from map %p", index, self->wrapped.self); + return avro_value_get_by_index(&self->wrapped, index, (avro_value_t *) child->self, name); +} + +static int +avro_resolved_map_reader_get_by_name(const avro_value_iface_t *viface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + int rval; + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_map_reader_t *miface = + container_of(iface, avro_resolved_map_reader_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + + /* + * This is a bit convoluted. We need to stash the wrapped child + * value somewhere in our children array. But we don't know + * where to put it until the wrapped map tells us what its index + * is. + */ + + avro_value_t real_child; + size_t real_index; + + DEBUG("Getting element %s from map %p", name, self->wrapped.self); + check(rval, avro_value_get_by_name + (&self->wrapped, name, &real_child, &real_index)); + + /* + * Ensure that our child wrapper array is big enough to hold + * this many elements. + */ + check(rval, avro_raw_array_ensure_size0(&self->children, real_index+1)); + if (avro_raw_array_size(&self->children) <= real_index) { + avro_raw_array_size(&self->children) = real_index+1; + } + + child->iface = &miface->child_resolver->parent; + child->self = avro_raw_array_get_raw(&self->children, real_index); + avro_value_t *child_vself = (avro_value_t *) child->self; + *child_vself = real_child; + + if (index != NULL) { + *index = real_index; + } + return 0; +} + +static avro_resolved_map_reader_t * +avro_resolved_map_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_map_reader_t); + memset(self, 0, sizeof(avro_resolved_map_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_reader_get_type; + self->parent.get_schema = avro_resolved_reader_get_schema; + self->parent.get_size = avro_resolved_map_reader_get_size; + self->parent.get_by_index = avro_resolved_map_reader_get_by_index; + self->parent.get_by_name = avro_resolved_map_reader_get_by_name; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->calculate_size = avro_resolved_map_reader_calculate_size; + self->free_iface = avro_resolved_map_reader_free_iface; + self->init = avro_resolved_map_reader_init; + self->done = avro_resolved_map_reader_done; + self->reset_wrappers = avro_resolved_map_reader_reset; + return container_of(self, avro_resolved_map_reader_t, parent); +} + +static avro_resolved_reader_t * +try_map(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * First verify that the reader is an map. + */ + + if (!is_avro_map(wschema)) { + return 0; + } + + /* + * Map schemas have to have compatible element schemas to be + * compatible themselves. Try to create an resolver to check + * the compatibility. + */ + + avro_resolved_map_reader_t *mself = + avro_resolved_map_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, mself); + + avro_schema_t witems = avro_schema_map_values(wschema); + avro_schema_t ritems = avro_schema_map_values(rschema); + + avro_resolved_reader_t *item_resolver = + avro_resolved_reader_new_memoized(state, witems, ritems); + if (item_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&mself->parent.parent); + avro_prefix_error("Map values aren't compatible: "); + return NULL; + } + + /* + * The two schemas are compatible. Store the item schema's + * resolver into the child_resolver field. + */ + + mself->child_resolver = item_resolver; + return &mself->parent; +} + + +/*----------------------------------------------------------------------- + * record + */ + +typedef struct avro_resolved_record_reader { + avro_resolved_reader_t parent; + size_t field_count; + size_t *field_offsets; + avro_resolved_reader_t **field_resolvers; + size_t *index_mapping; +} avro_resolved_record_reader_t; + +typedef struct avro_resolved_record_value { + avro_value_t wrapped; + /* The rest of the struct is taken up by the inline storage + * needed for each field. */ +} avro_resolved_record_value_t; + +/** Return a pointer to the given field within a record struct. */ +#define avro_resolved_record_field(iface, rec, index) \ + (((char *) (rec)) + (iface)->field_offsets[(index)]) + + +static void +avro_resolved_record_reader_calculate_size(avro_resolved_reader_t *iface) +{ + avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + + /* + * Once we've figured out which reader fields we actually need, + * calculate an offset for each one. + */ + + size_t ri; + size_t next_offset = sizeof(avro_resolved_record_value_t); + for (ri = 0; ri < riface->field_count; ri++) { + riface->field_offsets[ri] = next_offset; + if (riface->field_resolvers[ri] != NULL) { + avro_resolved_reader_calculate_size + (riface->field_resolvers[ri]); + size_t field_size = + riface->field_resolvers[ri]->instance_size; + DEBUG("Field %" PRIsz " has size %" PRIsz, ri, field_size); + next_offset += field_size; + } else { + DEBUG("Field %" PRIsz " is being skipped", ri); + } + } + + DEBUG("Record has size %" PRIsz, next_offset); + iface->instance_size = next_offset; +} + + +static void +avro_resolved_record_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + + if (riface->field_offsets != NULL) { + avro_free(riface->field_offsets, + riface->field_count * sizeof(size_t)); + } + + if (riface->field_resolvers != NULL) { + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + DEBUG("Freeing field %" PRIsz " %p", i, + riface->field_resolvers[i]); + free_resolver(riface->field_resolvers[i], freeing); + } + } + avro_free(riface->field_resolvers, + riface->field_count * sizeof(avro_resolved_reader_t *)); + } + + if (riface->index_mapping != NULL) { + avro_free(riface->index_mapping, + riface->field_count * sizeof(size_t)); + } + + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_record_reader_t, iface); +} + +static int +avro_resolved_record_reader_init(const avro_resolved_reader_t *iface, void *vself) +{ + int rval; + const avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + /* Initialize each field */ + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + check(rval, avro_resolved_reader_init + (riface->field_resolvers[i], + avro_resolved_record_field(riface, self, i))); + } + } + + return 0; +} + +static void +avro_resolved_record_reader_done(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + /* Finalize each field */ + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + avro_resolved_reader_done + (riface->field_resolvers[i], + avro_resolved_record_field(riface, self, i)); + } + } +} + +static int +avro_resolved_record_reader_reset(const avro_resolved_reader_t *iface, void *vself) +{ + int rval; + const avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + /* Reset each field */ + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + check(rval, avro_resolved_reader_reset_wrappers + (riface->field_resolvers[i], + avro_resolved_record_field(riface, self, i))); + } + } + + return 0; +} + +static int +avro_resolved_record_reader_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(vself); + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + *size = riface->field_count; + return 0; +} + +static int +avro_resolved_record_reader_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_record_reader_t *riface = + container_of(iface, avro_resolved_record_reader_t, parent); + const avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + DEBUG("Getting reader field %" PRIsz " from record %p", index, self->wrapped.self); + if (riface->field_resolvers[index] == NULL) { + /* + * TODO: Return the default value if the writer record + * doesn't contain this field. + */ + DEBUG("Writer doesn't have field"); + avro_set_error("NIY: Default values"); + return EINVAL; + } + + size_t writer_index = riface->index_mapping[index]; + DEBUG(" Writer field is %" PRIsz, writer_index); + child->iface = &riface->field_resolvers[index]->parent; + child->self = avro_resolved_record_field(riface, self, index); + return avro_value_get_by_index(&self->wrapped, writer_index, (avro_value_t *) child->self, name); +} + +static int +avro_resolved_record_reader_get_by_name(const avro_value_iface_t *viface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + + int ri = avro_schema_record_field_get_index(iface->rschema, name); + if (ri == -1) { + avro_set_error("Record doesn't have field named %s", name); + return EINVAL; + } + + DEBUG("Reader field %s is at index %d", name, ri); + if (index != NULL) { + *index = ri; + } + return avro_resolved_record_reader_get_by_index(viface, vself, ri, child, NULL); +} + +static avro_resolved_record_reader_t * +avro_resolved_record_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_record_reader_t); + memset(self, 0, sizeof(avro_resolved_record_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_reader_get_type; + self->parent.get_schema = avro_resolved_reader_get_schema; + self->parent.get_size = avro_resolved_record_reader_get_size; + self->parent.get_by_index = avro_resolved_record_reader_get_by_index; + self->parent.get_by_name = avro_resolved_record_reader_get_by_name; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->calculate_size = avro_resolved_record_reader_calculate_size; + self->free_iface = avro_resolved_record_reader_free_iface; + self->init = avro_resolved_record_reader_init; + self->done = avro_resolved_record_reader_done; + self->reset_wrappers = avro_resolved_record_reader_reset; + return container_of(self, avro_resolved_record_reader_t, parent); +} + +static avro_resolved_reader_t * +try_record(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * First verify that the writer is also a record, and has the + * same name as the reader. + */ + + if (!is_avro_record(wschema)) { + return 0; + } + + const char *wname = avro_schema_name(wschema); + const char *rname = avro_schema_name(rschema); + + if (strcmp(wname, rname) != 0) { + return 0; + } + + /* + * Categorize the fields in the record schemas. Fields that are + * only in the writer are ignored. Fields that are only in the + * reader raise a schema mismatch error, unless the field has a + * default value. Fields that are in both are resolved + * recursively. + * + * The field_resolvers array will contain an avro_value_iface_t + * for each field in the reader schema. To build this array, we + * loop through the fields of the reader schema. If that field + * is also in the writer schema, we resolve them recursively, + * and store the resolver into the array. If the field isn't in + * the writer schema, we raise an error. (TODO: Eventually, + * we'll handle default values here.) After this loop finishes, + * any NULLs in the field_resolvers array will represent fields + * in the writer but not the reader; these fields should be + * skipped, and won't be accessible in the resolved reader. + */ + + avro_resolved_record_reader_t *rself = + avro_resolved_record_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, rself); + + size_t rfields = avro_schema_record_size(rschema); + + DEBUG("Checking reader record schema %s", wname); + + avro_resolved_reader_t **field_resolvers = + (avro_resolved_reader_t **) avro_calloc(rfields, sizeof(avro_resolved_reader_t *)); + size_t *field_offsets = (size_t *) avro_calloc(rfields, sizeof(size_t)); + size_t *index_mapping = (size_t *) avro_calloc(rfields, sizeof(size_t)); + + size_t ri; + for (ri = 0; ri < rfields; ri++) { + avro_schema_t rfield = + avro_schema_record_field_get_by_index(rschema, ri); + const char *field_name = + avro_schema_record_field_name(rschema, ri); + + DEBUG("Resolving reader record field %" PRIsz " (%s)", ri, field_name); + + /* + * See if this field is also in the writer schema. + */ + + int wi = avro_schema_record_field_get_index(wschema, field_name); + + if (wi == -1) { + /* + * This field isn't in the writer schema — + * that's an error! TODO: Handle default + * values! + */ + + DEBUG("Field %s isn't in writer", field_name); + avro_set_error("Reader field %s doesn't appear in writer", + field_name); + goto error; + } + + /* + * Try to recursively resolve the schemas for this + * field. If they're not compatible, that's an error. + */ + + avro_schema_t wfield = + avro_schema_record_field_get_by_index(wschema, wi); + avro_resolved_reader_t *field_resolver = + avro_resolved_reader_new_memoized(state, wfield, rfield); + + if (field_resolver == NULL) { + avro_prefix_error("Field %s isn't compatible: ", field_name); + goto error; + } + + /* + * Save the details for this field. + */ + + DEBUG("Found match for field %s (%" PRIsz " in reader, %d in writer)", + field_name, ri, wi); + field_resolvers[ri] = field_resolver; + index_mapping[ri] = wi; + } + + /* + * We might not have found matches for all of the writer fields, + * but that's okay — any extras will be ignored. + */ + + rself->field_count = rfields; + rself->field_offsets = field_offsets; + rself->field_resolvers = field_resolvers; + rself->index_mapping = index_mapping; + return &rself->parent; + +error: + /* + * Clean up any resolver we might have already created. + */ + + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&rself->parent.parent); + + { + unsigned int i; + for (i = 0; i < rfields; i++) { + if (field_resolvers[i]) { + avro_value_iface_decref(&field_resolvers[i]->parent); + } + } + } + + avro_free(field_resolvers, rfields * sizeof(avro_resolved_reader_t *)); + avro_free(field_offsets, rfields * sizeof(size_t)); + avro_free(index_mapping, rfields * sizeof(size_t)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * writer union + */ + +/* + * For writer unions, we maintain a list of resolvers for each branch of + * the union. When we encounter a writer value, we see which branch it + * is, and choose a reader resolver based on that. + */ + +typedef struct avro_resolved_wunion_reader { + avro_resolved_reader_t parent; + + /* The number of branches in the writer union */ + size_t branch_count; + + /* A child resolver for each branch of the writer union. If any + * of these are NULL, then we don't have anything on the reader + * side that's compatible with that writer branch. */ + avro_resolved_reader_t **branch_resolvers; + +} avro_resolved_wunion_reader_t; + +typedef struct avro_resolved_wunion_value { + avro_value_t wrapped; + + /** The currently active branch of the union. -1 if no branch + * is selected. */ + int discriminant; + + /* The rest of the struct is taken up by the inline storage + * needed for the active branch. */ +} avro_resolved_wunion_value_t; + +/** Return a pointer to the active branch within a union struct. */ +#define avro_resolved_wunion_branch(_wunion) \ + (((char *) (_wunion)) + sizeof(avro_resolved_wunion_value_t)) + + +static void +avro_resolved_wunion_reader_calculate_size(avro_resolved_reader_t *iface) +{ + avro_resolved_wunion_reader_t *uiface = + container_of(iface, avro_resolved_wunion_reader_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + + size_t i; + size_t max_branch_size = 0; + for (i = 0; i < uiface->branch_count; i++) { + if (uiface->branch_resolvers[i] == NULL) { + DEBUG("No match for writer union branch %" PRIsz, i); + } else { + avro_resolved_reader_calculate_size + (uiface->branch_resolvers[i]); + size_t branch_size = + uiface->branch_resolvers[i]->instance_size; + DEBUG("Writer branch %" PRIsz " has size %" PRIsz, i, branch_size); + if (branch_size > max_branch_size) { + max_branch_size = branch_size; + } + } + } + + DEBUG("Maximum branch size is %" PRIsz, max_branch_size); + iface->instance_size = + sizeof(avro_resolved_wunion_value_t) + max_branch_size; + DEBUG("Total union size is %" PRIsz, iface->instance_size); +} + + +static void +avro_resolved_wunion_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + avro_resolved_wunion_reader_t *uiface = + container_of(iface, avro_resolved_wunion_reader_t, parent); + + if (uiface->branch_resolvers != NULL) { + size_t i; + for (i = 0; i < uiface->branch_count; i++) { + if (uiface->branch_resolvers[i] != NULL) { + free_resolver(uiface->branch_resolvers[i], freeing); + } + } + avro_free(uiface->branch_resolvers, + uiface->branch_count * sizeof(avro_resolved_reader_t *)); + } + + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_wunion_reader_t, iface); +} + +static int +avro_resolved_wunion_reader_init(const avro_resolved_reader_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_resolved_wunion_value_t *self = (avro_resolved_wunion_value_t *) vself; + self->discriminant = -1; + return 0; +} + +static void +avro_resolved_wunion_reader_done(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_wunion_reader_t *uiface = + container_of(iface, avro_resolved_wunion_reader_t, parent); + avro_resolved_wunion_value_t *self = (avro_resolved_wunion_value_t *) vself; + if (self->discriminant >= 0) { + avro_resolved_reader_done + (uiface->branch_resolvers[self->discriminant], + avro_resolved_wunion_branch(self)); + self->discriminant = -1; + } +} + +static int +avro_resolved_wunion_reader_reset(const avro_resolved_reader_t *iface, void *vself) +{ + const avro_resolved_wunion_reader_t *uiface = + container_of(iface, avro_resolved_wunion_reader_t, parent); + avro_resolved_wunion_value_t *self = (avro_resolved_wunion_value_t *) vself; + + /* Keep the same branch selected, for the common case that we're + * about to reuse it. */ + if (self->discriminant >= 0) { + return avro_resolved_reader_reset_wrappers + (uiface->branch_resolvers[self->discriminant], + avro_resolved_wunion_branch(self)); + } + + return 0; +} + +static int +avro_resolved_wunion_get_real_src(const avro_value_iface_t *viface, + const void *vself, avro_value_t *real_src) +{ + int rval; + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_wunion_reader_t *uiface = + container_of(iface, avro_resolved_wunion_reader_t, parent); + avro_resolved_wunion_value_t *self = (avro_resolved_wunion_value_t *) vself; + int writer_disc; + check(rval, avro_value_get_discriminant(&self->wrapped, &writer_disc)); + DEBUG("Writer is branch %d", writer_disc); + + if (uiface->branch_resolvers[writer_disc] == NULL) { + avro_set_error("Reader isn't compatible with writer branch %d", + writer_disc); + return EINVAL; + } + + if (self->discriminant == writer_disc) { + DEBUG("Writer branch %d already selected", writer_disc); + } else { + if (self->discriminant >= 0) { + DEBUG("Finalizing old writer branch %d", self->discriminant); + avro_resolved_reader_done + (uiface->branch_resolvers[self->discriminant], + avro_resolved_wunion_branch(self)); + } + DEBUG("Initializing writer branch %d", writer_disc); + check(rval, avro_resolved_reader_init + (uiface->branch_resolvers[writer_disc], + avro_resolved_wunion_branch(self))); + self->discriminant = writer_disc; + } + + real_src->iface = &uiface->branch_resolvers[writer_disc]->parent; + real_src->self = avro_resolved_wunion_branch(self); + return avro_value_get_current_branch(&self->wrapped, (avro_value_t *) real_src->self); +} + +static int +avro_resolved_wunion_reader_get_boolean(const avro_value_iface_t *viface, + const void *vself, int *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_boolean(&src, out); +} + +static int +avro_resolved_wunion_reader_get_bytes(const avro_value_iface_t *viface, + const void *vself, const void **buf, size_t *size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_bytes(&src, buf, size); +} + +static int +avro_resolved_wunion_reader_grab_bytes(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_grab_bytes(&src, dest); +} + +static int +avro_resolved_wunion_reader_get_double(const avro_value_iface_t *viface, + const void *vself, double *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_double(&src, out); +} + +static int +avro_resolved_wunion_reader_get_float(const avro_value_iface_t *viface, + const void *vself, float *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_float(&src, out); +} + +static int +avro_resolved_wunion_reader_get_int(const avro_value_iface_t *viface, + const void *vself, int32_t *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_int(&src, out); +} + +static int +avro_resolved_wunion_reader_get_long(const avro_value_iface_t *viface, + const void *vself, int64_t *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_long(&src, out); +} + +static int +avro_resolved_wunion_reader_get_null(const avro_value_iface_t *viface, + const void *vself) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_null(&src); +} + +static int +avro_resolved_wunion_reader_get_string(const avro_value_iface_t *viface, + const void *vself, const char **str, size_t *size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_string(&src, str, size); +} + +static int +avro_resolved_wunion_reader_grab_string(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_grab_string(&src, dest); +} + +static int +avro_resolved_wunion_reader_get_enum(const avro_value_iface_t *viface, + const void *vself, int *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_enum(&src, out); +} + +static int +avro_resolved_wunion_reader_get_fixed(const avro_value_iface_t *viface, + const void *vself, const void **buf, size_t *size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_fixed(&src, buf, size); +} + +static int +avro_resolved_wunion_reader_grab_fixed(const avro_value_iface_t *viface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_grab_fixed(&src, dest); +} + +static int +avro_resolved_wunion_reader_set_boolean(const avro_value_iface_t *viface, + void *vself, int val) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_boolean(&src, val); +} + +static int +avro_resolved_wunion_reader_set_bytes(const avro_value_iface_t *viface, + void *vself, void *buf, size_t size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_bytes(&src, buf, size); +} + +static int +avro_resolved_wunion_reader_give_bytes(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *buf) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_give_bytes(&src, buf); +} + +static int +avro_resolved_wunion_reader_set_double(const avro_value_iface_t *viface, + void *vself, double val) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_double(&src, val); +} + +static int +avro_resolved_wunion_reader_set_float(const avro_value_iface_t *viface, + void *vself, float val) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_float(&src, val); +} + +static int +avro_resolved_wunion_reader_set_int(const avro_value_iface_t *viface, + void *vself, int32_t val) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_int(&src, val); +} + +static int +avro_resolved_wunion_reader_set_long(const avro_value_iface_t *viface, + void *vself, int64_t val) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_long(&src, val); +} + +static int +avro_resolved_wunion_reader_set_null(const avro_value_iface_t *viface, + void *vself) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_null(&src); +} + +static int +avro_resolved_wunion_reader_set_string(const avro_value_iface_t *viface, + void *vself, const char *str) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_string(&src, str); +} + +static int +avro_resolved_wunion_reader_set_string_len(const avro_value_iface_t *viface, + void *vself, const char *str, size_t size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_string_len(&src, str, size); +} + +static int +avro_resolved_wunion_reader_give_string_len(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *buf) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_give_string_len(&src, buf); +} + +static int +avro_resolved_wunion_reader_set_enum(const avro_value_iface_t *viface, + void *vself, int val) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_enum(&src, val); +} + +static int +avro_resolved_wunion_reader_set_fixed(const avro_value_iface_t *viface, + void *vself, void *buf, size_t size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_fixed(&src, buf, size); +} + +static int +avro_resolved_wunion_reader_give_fixed(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *dest) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_give_fixed(&src, dest); +} + +static int +avro_resolved_wunion_reader_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_size(&src, size); +} + +static int +avro_resolved_wunion_reader_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_by_index(&src, index, child, name); +} + +static int +avro_resolved_wunion_reader_get_by_name(const avro_value_iface_t *viface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_by_name(&src, name, child, index); +} + +static int +avro_resolved_wunion_reader_get_discriminant(const avro_value_iface_t *viface, + const void *vself, int *out) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_discriminant(&src, out); +} + +static int +avro_resolved_wunion_reader_get_current_branch(const avro_value_iface_t *viface, + const void *vself, avro_value_t *branch) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_get_current_branch(&src, branch); +} + +static int +avro_resolved_wunion_reader_append(const avro_value_iface_t *viface, + void *vself, avro_value_t *child_out, + size_t *new_index) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_append(&src, child_out, new_index); +} + +static int +avro_resolved_wunion_reader_add(const avro_value_iface_t *viface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_add(&src, key, child, index, is_new); +} + +static int +avro_resolved_wunion_reader_set_branch(const avro_value_iface_t *viface, + void *vself, int discriminant, + avro_value_t *branch) +{ + int rval; + avro_value_t src; + check(rval, avro_resolved_wunion_get_real_src(viface, vself, &src)); + return avro_value_set_branch(&src, discriminant, branch); +} + +static avro_resolved_wunion_reader_t * +avro_resolved_wunion_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_wunion_reader_t); + memset(self, 0, sizeof(avro_resolved_wunion_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_reader_get_type; + self->parent.get_schema = avro_resolved_reader_get_schema; + + self->parent.get_boolean = avro_resolved_wunion_reader_get_boolean; + self->parent.grab_bytes = avro_resolved_wunion_reader_grab_bytes; + self->parent.get_bytes = avro_resolved_wunion_reader_get_bytes; + self->parent.get_double = avro_resolved_wunion_reader_get_double; + self->parent.get_float = avro_resolved_wunion_reader_get_float; + self->parent.get_int = avro_resolved_wunion_reader_get_int; + self->parent.get_long = avro_resolved_wunion_reader_get_long; + self->parent.get_null = avro_resolved_wunion_reader_get_null; + self->parent.get_string = avro_resolved_wunion_reader_get_string; + self->parent.grab_string = avro_resolved_wunion_reader_grab_string; + self->parent.get_enum = avro_resolved_wunion_reader_get_enum; + self->parent.get_fixed = avro_resolved_wunion_reader_get_fixed; + self->parent.grab_fixed = avro_resolved_wunion_reader_grab_fixed; + + self->parent.set_boolean = avro_resolved_wunion_reader_set_boolean; + self->parent.set_bytes = avro_resolved_wunion_reader_set_bytes; + self->parent.give_bytes = avro_resolved_wunion_reader_give_bytes; + self->parent.set_double = avro_resolved_wunion_reader_set_double; + self->parent.set_float = avro_resolved_wunion_reader_set_float; + self->parent.set_int = avro_resolved_wunion_reader_set_int; + self->parent.set_long = avro_resolved_wunion_reader_set_long; + self->parent.set_null = avro_resolved_wunion_reader_set_null; + self->parent.set_string = avro_resolved_wunion_reader_set_string; + self->parent.set_string_len = avro_resolved_wunion_reader_set_string_len; + self->parent.give_string_len = avro_resolved_wunion_reader_give_string_len; + self->parent.set_enum = avro_resolved_wunion_reader_set_enum; + self->parent.set_fixed = avro_resolved_wunion_reader_set_fixed; + self->parent.give_fixed = avro_resolved_wunion_reader_give_fixed; + + self->parent.get_size = avro_resolved_wunion_reader_get_size; + self->parent.get_by_index = avro_resolved_wunion_reader_get_by_index; + self->parent.get_by_name = avro_resolved_wunion_reader_get_by_name; + self->parent.get_discriminant = avro_resolved_wunion_reader_get_discriminant; + self->parent.get_current_branch = avro_resolved_wunion_reader_get_current_branch; + + self->parent.append = avro_resolved_wunion_reader_append; + self->parent.add = avro_resolved_wunion_reader_add; + self->parent.set_branch = avro_resolved_wunion_reader_set_branch; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->calculate_size = avro_resolved_wunion_reader_calculate_size; + self->free_iface = avro_resolved_wunion_reader_free_iface; + self->init = avro_resolved_wunion_reader_init; + self->done = avro_resolved_wunion_reader_done; + self->reset_wrappers = avro_resolved_wunion_reader_reset; + return container_of(self, avro_resolved_wunion_reader_t, parent); +} + +static avro_resolved_reader_t * +try_writer_union(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * For a writer union, we check each branch of the union in turn + * against the reader schema. For each one that is compatible, + * we save the child resolver that can be used to process a + * writer value of that branch. + */ + + size_t branch_count = avro_schema_union_size(wschema); + DEBUG("Checking %" PRIsz "-branch writer union schema", branch_count); + + avro_resolved_wunion_reader_t *uself = + avro_resolved_wunion_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, uself); + + avro_resolved_reader_t **branch_resolvers = + (avro_resolved_reader_t **) avro_calloc(branch_count, sizeof(avro_resolved_reader_t *)); + int some_branch_compatible = 0; + + size_t i; + for (i = 0; i < branch_count; i++) { + avro_schema_t branch_schema = + avro_schema_union_branch(wschema, i); + + DEBUG("Resolving writer union branch %" PRIsz " (%s)", i, + avro_schema_type_name(branch_schema)); + + /* + * Try to recursively resolve this branch of the writer + * union against the reader schema. Don't raise + * an error if this fails — we just need one of + * the writer branches to be compatible. + */ + + branch_resolvers[i] = + avro_resolved_reader_new_memoized(state, branch_schema, rschema); + if (branch_resolvers[i] == NULL) { + DEBUG("No match for writer union branch %" PRIsz, i); + } else { + DEBUG("Found match for writer union branch %" PRIsz, i); + some_branch_compatible = 1; + } + } + + /* + * If we didn't find a match, that's an error. + */ + + if (!some_branch_compatible) { + DEBUG("No writer union branches match"); + avro_set_error("No branches in the writer are compatible " + "with reader schema %s", + avro_schema_type_name(rschema)); + goto error; + } + + uself->branch_count = branch_count; + uself->branch_resolvers = branch_resolvers; + return &uself->parent; + +error: + /* + * Clean up any resolver we might have already created. + */ + + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&uself->parent.parent); + + { + unsigned int i; + for (i = 0; i < branch_count; i++) { + if (branch_resolvers[i]) { + avro_value_iface_decref(&branch_resolvers[i]->parent); + } + } + } + + avro_free(branch_resolvers, branch_count * sizeof(avro_resolved_reader_t *)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * reader union + */ + +/* + * For reader unions, we only resolve them against writers which aren't + * unions. (We'll have already broken any writer union apart into its + * separate branches.) We just have to record which branch of the + * reader union the writer schema is compatible with. + */ + +typedef struct avro_resolved_runion_reader { + avro_resolved_reader_t parent; + + /* The reader union branch that's compatible with the writer + * schema. */ + size_t active_branch; + + /* A child resolver for the reader branch. */ + avro_resolved_reader_t *branch_resolver; +} avro_resolved_runion_reader_t; + + +static void +avro_resolved_runion_reader_calculate_size(avro_resolved_reader_t *iface) +{ + avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + + avro_resolved_reader_calculate_size(uiface->branch_resolver); + iface->instance_size = uiface->branch_resolver->instance_size; +} + + +static void +avro_resolved_runion_reader_free_iface(avro_resolved_reader_t *iface, st_table *freeing) +{ + avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + + if (uiface->branch_resolver != NULL) { + free_resolver(uiface->branch_resolver, freeing); + } + + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_runion_reader_t, iface); +} + +static int +avro_resolved_runion_reader_init(const avro_resolved_reader_t *iface, void *vself) +{ + avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + return avro_resolved_reader_init(uiface->branch_resolver, vself); +} + +static void +avro_resolved_runion_reader_done(const avro_resolved_reader_t *iface, void *vself) +{ + avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + avro_resolved_reader_done(uiface->branch_resolver, vself); +} + +static int +avro_resolved_runion_reader_reset(const avro_resolved_reader_t *iface, void *vself) +{ + avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + return avro_resolved_reader_reset_wrappers(uiface->branch_resolver, vself); +} + +static int +avro_resolved_runion_reader_get_discriminant(const avro_value_iface_t *viface, + const void *vself, int *out) +{ + AVRO_UNUSED(vself); + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + DEBUG("Reader union is branch %" PRIsz, uiface->active_branch); + *out = uiface->active_branch; + return 0; +} + +static int +avro_resolved_runion_reader_get_current_branch(const avro_value_iface_t *viface, + const void *vself, avro_value_t *branch) +{ + const avro_resolved_reader_t *iface = + container_of(viface, avro_resolved_reader_t, parent); + const avro_resolved_runion_reader_t *uiface = + container_of(iface, avro_resolved_runion_reader_t, parent); + DEBUG("Getting reader branch %" PRIsz " for union %p", uiface->active_branch, vself); + branch->iface = &uiface->branch_resolver->parent; + branch->self = (void *) vself; + return 0; +} + +static avro_resolved_runion_reader_t * +avro_resolved_runion_reader_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_reader_t *self = (avro_resolved_reader_t *) avro_new(avro_resolved_runion_reader_t); + memset(self, 0, sizeof(avro_resolved_runion_reader_t)); + + self->parent.incref_iface = avro_resolved_reader_incref_iface; + self->parent.decref_iface = avro_resolved_reader_decref_iface; + self->parent.incref = avro_resolved_reader_incref; + self->parent.decref = avro_resolved_reader_decref; + self->parent.reset = avro_resolved_reader_reset; + self->parent.get_type = avro_resolved_reader_get_type; + self->parent.get_schema = avro_resolved_reader_get_schema; + self->parent.get_discriminant = avro_resolved_runion_reader_get_discriminant; + self->parent.get_current_branch = avro_resolved_runion_reader_get_current_branch; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->calculate_size = avro_resolved_runion_reader_calculate_size; + self->free_iface = avro_resolved_runion_reader_free_iface; + self->init = avro_resolved_runion_reader_init; + self->done = avro_resolved_runion_reader_done; + self->reset_wrappers = avro_resolved_runion_reader_reset; + return container_of(self, avro_resolved_runion_reader_t, parent); +} + +static avro_resolved_reader_t * +try_reader_union(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * For a reader union, we have to identify which branch + * corresponds to the writer schema. (The writer won't be a + * union, since we'll have already broken it into its branches.) + */ + + size_t branch_count = avro_schema_union_size(rschema); + DEBUG("Checking %" PRIsz "-branch reader union schema", branch_count); + + avro_resolved_runion_reader_t *uself = + avro_resolved_runion_reader_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, uself); + + size_t i; + for (i = 0; i < branch_count; i++) { + avro_schema_t branch_schema = + avro_schema_union_branch(rschema, i); + + DEBUG("Resolving reader union branch %" PRIsz " (%s)", i, + avro_schema_type_name(branch_schema)); + + /* + * Try to recursively resolve this branch of the reader + * union against the writer schema. Don't raise + * an error if this fails — we just need one of + * the reader branches to be compatible. + */ + + uself->branch_resolver = + avro_resolved_reader_new_memoized(state, wschema, branch_schema); + if (uself->branch_resolver == NULL) { + DEBUG("No match for reader union branch %" PRIsz, i); + } else { + DEBUG("Found match for reader union branch %" PRIsz, i); + uself->active_branch = i; + return &uself->parent; + } + } + + /* + * If we didn't find a match, that's an error. + */ + + DEBUG("No reader union branches match"); + avro_set_error("No branches in the reader are compatible " + "with writer schema %s", + avro_schema_type_name(wschema)); + goto error; + +error: + /* + * Clean up any resolver we might have already created. + */ + + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&uself->parent.parent); + return NULL; +} + + +/*----------------------------------------------------------------------- + * Schema type dispatcher + */ + +static avro_resolved_reader_t * +avro_resolved_reader_new_memoized(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + check_param(NULL, is_avro_schema(wschema), "writer schema"); + check_param(NULL, is_avro_schema(rschema), "reader schema"); + + /* + * First see if we've already matched these two schemas. If so, + * just return that resolver. + */ + + avro_resolved_reader_t *saved = NULL; + if (avro_memoize_get(&state->mem, wschema, rschema, (void **) &saved)) { + DEBUG("Already resolved %s%s%s->%s%s%s", + is_avro_link(wschema)? "[": "", + avro_schema_type_name(wschema), + is_avro_link(wschema)? "]": "", + is_avro_link(rschema)? "[": "", + avro_schema_type_name(rschema), + is_avro_link(rschema)? "]": ""); + return saved; + } else { + DEBUG("Resolving %s%s%s->%s%s%s", + is_avro_link(wschema)? "[": "", + avro_schema_type_name(wschema), + is_avro_link(wschema)? "]": "", + is_avro_link(rschema)? "[": "", + avro_schema_type_name(rschema), + is_avro_link(rschema)? "]": ""); + } + + /* + * Otherwise we have some work to do. First check if the writer + * schema is a union. If so, break it apart. + */ + + if (is_avro_union(wschema)) { + return try_writer_union(state, wschema, rschema); + } + + else if (is_avro_link(wschema)) { + return try_wlink(state, wschema, rschema); + } + + /* + * If the writer isn't a union, than choose a resolver based on + * the reader schema. + */ + + switch (avro_typeof(rschema)) + { + case AVRO_BOOLEAN: + return try_boolean(state, wschema, rschema); + + case AVRO_BYTES: + return try_bytes(state, wschema, rschema); + + case AVRO_DOUBLE: + return try_double(state, wschema, rschema); + + case AVRO_FLOAT: + return try_float(state, wschema, rschema); + + case AVRO_INT32: + return try_int(state, wschema, rschema); + + case AVRO_INT64: + return try_long(state, wschema, rschema); + + case AVRO_NULL: + return try_null(state, wschema, rschema); + + case AVRO_STRING: + return try_string(state, wschema, rschema); + + case AVRO_ARRAY: + return try_array(state, wschema, rschema); + + case AVRO_ENUM: + return try_enum(state, wschema, rschema); + + case AVRO_FIXED: + return try_fixed(state, wschema, rschema); + + case AVRO_MAP: + return try_map(state, wschema, rschema); + + case AVRO_RECORD: + return try_record(state, wschema, rschema); + + case AVRO_UNION: + return try_reader_union(state, wschema, rschema); + + case AVRO_LINK: + return try_rlink(state, wschema, rschema); + + default: + avro_set_error("Unknown reader schema type"); + return NULL; + } + + return NULL; +} + + +avro_value_iface_t * +avro_resolved_reader_new(avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * Create a state to keep track of the value implementations + * that we create for each subschema. + */ + + memoize_state_t state; + avro_memoize_init(&state.mem); + state.links = NULL; + + /* + * Create the value implementations. + */ + + avro_resolved_reader_t *result = + avro_resolved_reader_new_memoized(&state, wschema, rschema); + if (result == NULL) { + avro_memoize_done(&state.mem); + return NULL; + } + + /* + * Fix up any link schemas so that their value implementations + * point to their target schemas' implementations. + */ + + avro_resolved_reader_calculate_size(result); + while (state.links != NULL) { + avro_resolved_link_reader_t *liface = state.links; + avro_resolved_reader_calculate_size(liface->target_resolver); + state.links = liface->next; + liface->next = NULL; + } + + /* + * And now we can return. + */ + + avro_memoize_done(&state.mem); + return &result->parent; +} diff --git a/fluent-bit/lib/avro/src/resolved-writer.c b/fluent-bit/lib/avro/src/resolved-writer.c new file mode 100644 index 000000000..0eafba00d --- /dev/null +++ b/fluent-bit/lib/avro/src/resolved-writer.c @@ -0,0 +1,2911 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro_private.h" +#include "avro/allocation.h" +#include "avro/basics.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro/refcount.h" +#include "avro/resolver.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "st.h" + +#ifndef AVRO_RESOLVER_DEBUG +#define AVRO_RESOLVER_DEBUG 0 +#endif + +#if AVRO_RESOLVER_DEBUG +#include <stdio.h> +#define DEBUG(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (0) +#else +#define DEBUG(...) /* don't print messages */ +#endif + + +typedef struct avro_resolved_writer avro_resolved_writer_t; + +struct avro_resolved_writer { + avro_value_iface_t parent; + + /** The reference count for this interface. */ + volatile int refcount; + + /** The writer schema. */ + avro_schema_t wschema; + + /** The reader schema. */ + avro_schema_t rschema; + + /* If the reader schema is a union, but the writer schema is + * not, this field indicates which branch of the reader union + * should be selected. */ + int reader_union_branch; + + /* The size of the value instances for this resolver. */ + size_t instance_size; + + /* A function to calculate the instance size once the overall + * top-level resolver (and all of its children) have been + * constructed. */ + void + (*calculate_size)(avro_resolved_writer_t *iface); + + /* A free function for this resolver interface */ + void + (*free_iface)(avro_resolved_writer_t *iface, st_table *freeing); + + /* An initialization function for instances of this resolver. */ + int + (*init)(const avro_resolved_writer_t *iface, void *self); + + /* A finalization function for instances of this resolver. */ + void + (*done)(const avro_resolved_writer_t *iface, void *self); + + /* Clear out any existing wrappers, if any */ + int + (*reset_wrappers)(const avro_resolved_writer_t *iface, void *self); +}; + +#define avro_resolved_writer_calculate_size(iface) \ + do { \ + if ((iface)->calculate_size != NULL) { \ + (iface)->calculate_size((iface)); \ + } \ + } while (0) +#define avro_resolved_writer_init(iface, self) \ + ((iface)->init == NULL? 0: (iface)->init((iface), (self))) +#define avro_resolved_writer_done(iface, self) \ + ((iface)->done == NULL? (void) 0: (iface)->done((iface), (self))) +#define avro_resolved_writer_reset_wrappers(iface, self) \ + ((iface)->reset_wrappers == NULL? 0: \ + (iface)->reset_wrappers((iface), (self))) + + +/* + * We assume that each instance type in this value contains an an + * avro_value_t as its first element, which is the current wrapped + * value. + */ + +void +avro_resolved_writer_set_dest(avro_value_t *resolved, + avro_value_t *dest) +{ + avro_value_t *self = (avro_value_t *) resolved->self; + if (self->self != NULL) { + avro_value_decref(self); + } + avro_value_copy_ref(self, dest); +} + +void +avro_resolved_writer_clear_dest(avro_value_t *resolved) +{ + avro_value_t *self = (avro_value_t *) resolved->self; + if (self->self != NULL) { + avro_value_decref(self); + } + self->iface = NULL; + self->self = NULL; +} + +int +avro_resolved_writer_new_value(avro_value_iface_t *viface, + avro_value_t *value) +{ + int rval; + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + void *self = avro_malloc(iface->instance_size + sizeof(volatile int)); + if (self == NULL) { + value->iface = NULL; + value->self = NULL; + return ENOMEM; + } + + memset(self, 0, iface->instance_size + sizeof(volatile int)); + volatile int *refcount = (volatile int *) self; + self = (char *) self + sizeof(volatile int); + + rval = avro_resolved_writer_init(iface, self); + if (rval != 0) { + avro_free(self, iface->instance_size + sizeof(volatile int)); + value->iface = NULL; + value->self = NULL; + return rval; + } + + *refcount = 1; + value->iface = avro_value_iface_incref(viface); + value->self = self; + return 0; +} + +static void +avro_resolved_writer_free_value(const avro_value_iface_t *viface, void *vself) +{ + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + + avro_resolved_writer_done(iface, vself); + if (self->self != NULL) { + avro_value_decref(self); + } + + vself = (char *) vself - sizeof(volatile int); + avro_free(vself, iface->instance_size + sizeof(volatile int)); +} + +static void +avro_resolved_writer_incref(avro_value_t *value) +{ + /* + * This only works if you pass in the top-level value. + */ + + volatile int *refcount = (volatile int *) ((char *) value->self - sizeof(volatile int)); + avro_refcount_inc(refcount); +} + +static void +avro_resolved_writer_decref(avro_value_t *value) +{ + /* + * This only works if you pass in the top-level value. + */ + + volatile int *refcount = (volatile int *) ((char *) value->self - sizeof(volatile int)); + if (avro_refcount_dec(refcount)) { + avro_resolved_writer_free_value(value->iface, value->self); + } +} + + +static avro_value_iface_t * +avro_resolved_writer_incref_iface(avro_value_iface_t *viface) +{ + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_refcount_inc(&iface->refcount); + return viface; +} + +static void +free_resolver(avro_resolved_writer_t *iface, st_table *freeing) +{ + /* First check if we've already started freeing this resolver. */ + if (st_lookup(freeing, (st_data_t) iface, NULL)) { + DEBUG("Already freed %p", iface); + return; + } + + /* Otherwise add this resolver to the freeing set, then free it. */ + st_insert(freeing, (st_data_t) iface, (st_data_t) NULL); + DEBUG("Freeing resolver %p (%s->%s)", iface, + avro_schema_type_name(iface->wschema), + avro_schema_type_name(iface->rschema)); + + iface->free_iface(iface, freeing); +} + +static void +avro_resolved_writer_calculate_size_(avro_resolved_writer_t *iface) +{ + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_value_t); +} + +static void +avro_resolved_writer_free_iface(avro_resolved_writer_t *iface, st_table *freeing) +{ + AVRO_UNUSED(freeing); + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_writer_t, iface); +} + +static void +avro_resolved_writer_decref_iface(avro_value_iface_t *viface) +{ + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + DEBUG("Decref resolver %p (before=%d)", iface, iface->refcount); + if (avro_refcount_dec(&iface->refcount)) { + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + + st_table *freeing = st_init_numtable(); + free_resolver(iface, freeing); + st_free_table(freeing); + } +} + + +static int +avro_resolved_writer_reset(const avro_value_iface_t *viface, void *vself) +{ + /* + * To reset a wrapped value, we first clear out any wrappers, + * and then have the wrapped value reset itself. + */ + + int rval; + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + check(rval, avro_resolved_writer_reset_wrappers(iface, vself)); + return avro_value_reset(self); +} + +static avro_type_t +avro_resolved_writer_get_type(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(vself); + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + return avro_typeof(iface->wschema); +} + +static avro_schema_t +avro_resolved_writer_get_schema(const avro_value_iface_t *viface, const void *vself) +{ + AVRO_UNUSED(vself); + avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + return iface->wschema; +} + + +static avro_resolved_writer_t * +avro_resolved_writer_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_writer_t *self = (avro_resolved_writer_t *) avro_new(avro_resolved_writer_t); + memset(self, 0, sizeof(avro_resolved_writer_t)); + + self->parent.incref_iface = avro_resolved_writer_incref_iface; + self->parent.decref_iface = avro_resolved_writer_decref_iface; + self->parent.incref = avro_resolved_writer_incref; + self->parent.decref = avro_resolved_writer_decref; + self->parent.reset = avro_resolved_writer_reset; + self->parent.get_type = avro_resolved_writer_get_type; + self->parent.get_schema = avro_resolved_writer_get_schema; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->reader_union_branch = -1; + self->calculate_size = avro_resolved_writer_calculate_size_; + self->free_iface = avro_resolved_writer_free_iface; + self->reset_wrappers = NULL; + return self; +} + +static inline int +avro_resolved_writer_get_real_dest(const avro_resolved_writer_t *iface, + const avro_value_t *dest, avro_value_t *real_dest) +{ + if (iface->reader_union_branch < 0) { + /* + * The reader schema isn't a union, so use the dest + * field as-is. + */ + + *real_dest = *dest; + return 0; + } + + DEBUG("Retrieving union branch %d for %s value", + iface->reader_union_branch, + avro_schema_type_name(iface->wschema)); + + return avro_value_set_branch(dest, iface->reader_union_branch, real_dest); +} + + +#define skip_links(schema) \ + while (is_avro_link(schema)) { \ + schema = avro_schema_link_target(schema); \ + } + + +/*----------------------------------------------------------------------- + * Memoized resolvers + */ + +typedef struct avro_resolved_link_writer avro_resolved_link_writer_t; + +typedef struct memoize_state_t { + avro_memoize_t mem; + avro_resolved_link_writer_t *links; +} memoize_state_t; + +static avro_resolved_writer_t * +avro_resolved_writer_new_memoized(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema); + + +/*----------------------------------------------------------------------- + * Reader unions + */ + +/* + * For each Avro type, we have to check whether the reader schema on its + * own is compatible, and also whether the reader is a union that + * contains a compatible type. The macros in this section help us + * perform both of these checks with less code. + */ + + +/** + * A helper macro that handles the case where neither writer nor reader + * are unions. Uses @ref check_func to see if the two schemas are + * compatible. + */ + +#define check_non_union(saved, wschema, rschema, check_func) \ +do { \ + avro_resolved_writer_t *self = NULL; \ + int rc = check_func(saved, &self, wschema, rschema, \ + rschema); \ + if (self) { \ + DEBUG("Non-union schemas %s (writer) " \ + "and %s (reader) match", \ + avro_schema_type_name(wschema), \ + avro_schema_type_name(rschema)); \ + \ + self->reader_union_branch = -1; \ + return self; \ + } \ + \ + if (rc) { \ + return NULL; \ + } \ +} while (0) + + +/** + * Helper macro that handles the case where the reader is a union, and + * the writer is not. Checks each branch of the reader union schema, + * looking for the first branch that is compatible with the writer + * schema. The @ref check_func argument should be a function that can + * check the compatiblity of each branch schema. + */ + +#define check_reader_union(saved, wschema, rschema, check_func) \ +do { \ + if (!is_avro_union(rschema)) { \ + break; \ + } \ + \ + DEBUG("Checking reader union schema"); \ + size_t num_branches = avro_schema_union_size(rschema); \ + unsigned int i; \ + \ + for (i = 0; i < num_branches; i++) { \ + avro_schema_t branch_schema = \ + avro_schema_union_branch(rschema, i); \ + skip_links(branch_schema); \ + \ + DEBUG("Trying branch %u %s%s%s->%s", i, \ + is_avro_link(wschema)? "[": "", \ + avro_schema_type_name(wschema), \ + is_avro_link(wschema)? "]": "", \ + avro_schema_type_name(branch_schema)); \ + \ + avro_resolved_writer_t *self = NULL; \ + int rc = check_func(saved, &self, \ + wschema, branch_schema, rschema); \ + if (self) { \ + DEBUG("Reader union branch %d (%s) " \ + "and writer %s match", \ + i, avro_schema_type_name(branch_schema), \ + avro_schema_type_name(wschema)); \ + self->reader_union_branch = i; \ + return self; \ + } else { \ + DEBUG("Reader union branch %d (%s) " \ + "doesn't match", \ + i, avro_schema_type_name(branch_schema)); \ + } \ + \ + if (rc) { \ + return NULL; \ + } \ + } \ + \ + DEBUG("No reader union branches match"); \ +} while (0) + +/** + * A helper macro that wraps together check_non_union and + * check_reader_union for a simple (non-union) writer schema type. + */ + +#define check_simple_writer(saved, wschema, rschema, type_name) \ +do { \ + check_non_union(saved, wschema, rschema, try_##type_name); \ + check_reader_union(saved, wschema, rschema, try_##type_name); \ + DEBUG("Writer %s doesn't match reader %s", \ + avro_schema_type_name(wschema), \ + avro_schema_type_name(rschema)); \ + avro_set_error("Cannot store " #type_name " into %s", \ + avro_schema_type_name(rschema)); \ + return NULL; \ +} while (0) + + +/*----------------------------------------------------------------------- + * Recursive schemas + */ + +/* + * Recursive schemas are handled specially; the value implementation for + * an AVRO_LINK schema is simply a wrapper around the value + * implementation for the link's target schema. The value methods all + * delegate to the wrapped implementation. + */ + +struct avro_resolved_link_writer { + avro_resolved_writer_t parent; + + /** + * A pointer to the “next” link resolver that we've had to + * create. We use this as we're creating the overall top-level + * resolver to keep track of which ones we have to fix up + * afterwards. + */ + avro_resolved_link_writer_t *next; + + /** The target's implementation. */ + avro_resolved_writer_t *target_resolver; +}; + +typedef struct avro_resolved_link_value { + avro_value_t wrapped; + avro_value_t target; +} avro_resolved_link_value_t; + +static void +avro_resolved_link_writer_calculate_size(avro_resolved_writer_t *iface) +{ + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for [%s]->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_link_value_t); +} + +static void +avro_resolved_link_writer_free_iface(avro_resolved_writer_t *iface, st_table *freeing) +{ + avro_resolved_link_writer_t *liface = + container_of(iface, avro_resolved_link_writer_t, parent); + if (liface->target_resolver != NULL) { + free_resolver(liface->target_resolver, freeing); + } + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_link_writer_t, iface); +} + +static int +avro_resolved_link_writer_init(const avro_resolved_writer_t *iface, void *vself) +{ + int rval; + const avro_resolved_link_writer_t *liface = + container_of(iface, avro_resolved_link_writer_t, parent); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + size_t target_instance_size = liface->target_resolver->instance_size; + + self->target.iface = &liface->target_resolver->parent; + self->target.self = avro_malloc(target_instance_size); + if (self->target.self == NULL) { + return ENOMEM; + } + DEBUG("Allocated <%p:%" PRIsz "> for link", self->target.self, target_instance_size); + + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + + rval = avro_resolved_writer_init(liface->target_resolver, self->target.self); + if (rval != 0) { + avro_free(self->target.self, target_instance_size); + } + return rval; +} + +static void +avro_resolved_link_writer_done(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_link_writer_t *liface = + container_of(iface, avro_resolved_link_writer_t, parent); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + size_t target_instance_size = liface->target_resolver->instance_size; + DEBUG("Freeing <%p:%" PRIsz "> for link", self->target.self, target_instance_size); + avro_resolved_writer_done(liface->target_resolver, self->target.self); + avro_free(self->target.self, target_instance_size); + self->target.iface = NULL; + self->target.self = NULL; +} + +static int +avro_resolved_link_writer_reset(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_link_writer_t *liface = + container_of(iface, avro_resolved_link_writer_t, parent); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + return avro_resolved_writer_reset_wrappers + (liface->target_resolver, self->target.self); +} + +static avro_type_t +avro_resolved_link_writer_get_type(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_type(&self->target); +} + +static avro_schema_t +avro_resolved_link_writer_get_schema(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_schema(&self->target); +} + +static int +avro_resolved_link_writer_get_boolean(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_boolean(&self->target, out); +} + +static int +avro_resolved_link_writer_get_bytes(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_bytes(&self->target, buf, size); +} + +static int +avro_resolved_link_writer_grab_bytes(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_grab_bytes(&self->target, dest); +} + +static int +avro_resolved_link_writer_get_double(const avro_value_iface_t *iface, + const void *vself, double *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_double(&self->target, out); +} + +static int +avro_resolved_link_writer_get_float(const avro_value_iface_t *iface, + const void *vself, float *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_float(&self->target, out); +} + +static int +avro_resolved_link_writer_get_int(const avro_value_iface_t *iface, + const void *vself, int32_t *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_int(&self->target, out); +} + +static int +avro_resolved_link_writer_get_long(const avro_value_iface_t *iface, + const void *vself, int64_t *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_long(&self->target, out); +} + +static int +avro_resolved_link_writer_get_null(const avro_value_iface_t *iface, const void *vself) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_null(&self->target); +} + +static int +avro_resolved_link_writer_get_string(const avro_value_iface_t *iface, + const void *vself, const char **str, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_string(&self->target, str, size); +} + +static int +avro_resolved_link_writer_grab_string(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_grab_string(&self->target, dest); +} + +static int +avro_resolved_link_writer_get_enum(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_enum(&self->target, out); +} + +static int +avro_resolved_link_writer_get_fixed(const avro_value_iface_t *iface, + const void *vself, const void **buf, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_fixed(&self->target, buf, size); +} + +static int +avro_resolved_link_writer_grab_fixed(const avro_value_iface_t *iface, + const void *vself, avro_wrapped_buffer_t *dest) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_grab_fixed(&self->target, dest); +} + +static int +avro_resolved_link_writer_set_boolean(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_boolean(&self->target, val); +} + +static int +avro_resolved_link_writer_set_bytes(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_bytes(&self->target, buf, size); +} + +static int +avro_resolved_link_writer_give_bytes(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_give_bytes(&self->target, buf); +} + +static int +avro_resolved_link_writer_set_double(const avro_value_iface_t *iface, + void *vself, double val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_double(&self->target, val); +} + +static int +avro_resolved_link_writer_set_float(const avro_value_iface_t *iface, + void *vself, float val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_float(&self->target, val); +} + +static int +avro_resolved_link_writer_set_int(const avro_value_iface_t *iface, + void *vself, int32_t val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_int(&self->target, val); +} + +static int +avro_resolved_link_writer_set_long(const avro_value_iface_t *iface, + void *vself, int64_t val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_long(&self->target, val); +} + +static int +avro_resolved_link_writer_set_null(const avro_value_iface_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_null(&self->target); +} + +static int +avro_resolved_link_writer_set_string(const avro_value_iface_t *iface, + void *vself, const char *str) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_string(&self->target, str); +} + +static int +avro_resolved_link_writer_set_string_len(const avro_value_iface_t *iface, + void *vself, const char *str, size_t size) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_string_len(&self->target, str, size); +} + +static int +avro_resolved_link_writer_give_string_len(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_give_string_len(&self->target, buf); +} + +static int +avro_resolved_link_writer_set_enum(const avro_value_iface_t *iface, + void *vself, int val) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_enum(&self->target, val); +} + +static int +avro_resolved_link_writer_set_fixed(const avro_value_iface_t *iface, + void *vself, void *buf, size_t size) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_fixed(&self->target, buf, size); +} + +static int +avro_resolved_link_writer_give_fixed(const avro_value_iface_t *iface, + void *vself, avro_wrapped_buffer_t *buf) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_give_fixed(&self->target, buf); +} + +static int +avro_resolved_link_writer_get_size(const avro_value_iface_t *iface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_size(&self->target, size); +} + +static int +avro_resolved_link_writer_get_by_index(const avro_value_iface_t *iface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_by_index(&self->target, index, child, name); +} + +static int +avro_resolved_link_writer_get_by_name(const avro_value_iface_t *iface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_by_name(&self->target, name, child, index); +} + +static int +avro_resolved_link_writer_get_discriminant(const avro_value_iface_t *iface, + const void *vself, int *out) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_discriminant(&self->target, out); +} + +static int +avro_resolved_link_writer_get_current_branch(const avro_value_iface_t *iface, + const void *vself, avro_value_t *branch) +{ + AVRO_UNUSED(iface); + const avro_resolved_link_value_t *self = (const avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_get_current_branch(&self->target, branch); +} + +static int +avro_resolved_link_writer_append(const avro_value_iface_t *iface, + void *vself, avro_value_t *child_out, + size_t *new_index) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_append(&self->target, child_out, new_index); +} + +static int +avro_resolved_link_writer_add(const avro_value_iface_t *iface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_add(&self->target, key, child, index, is_new); +} + +static int +avro_resolved_link_writer_set_branch(const avro_value_iface_t *iface, + void *vself, int discriminant, + avro_value_t *branch) +{ + AVRO_UNUSED(iface); + avro_resolved_link_value_t *self = (avro_resolved_link_value_t *) vself; + avro_value_t *target_vself = (avro_value_t *) self->target.self; + *target_vself = self->wrapped; + return avro_value_set_branch(&self->target, discriminant, branch); +} + +static avro_resolved_link_writer_t * +avro_resolved_link_writer_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_writer_t *self = (avro_resolved_writer_t *) avro_new(avro_resolved_link_writer_t); + memset(self, 0, sizeof(avro_resolved_link_writer_t)); + + self->parent.incref_iface = avro_resolved_writer_incref_iface; + self->parent.decref_iface = avro_resolved_writer_decref_iface; + self->parent.incref = avro_resolved_writer_incref; + self->parent.decref = avro_resolved_writer_decref; + self->parent.reset = avro_resolved_writer_reset; + self->parent.get_type = avro_resolved_link_writer_get_type; + self->parent.get_schema = avro_resolved_link_writer_get_schema; + self->parent.get_size = avro_resolved_link_writer_get_size; + self->parent.get_by_index = avro_resolved_link_writer_get_by_index; + self->parent.get_by_name = avro_resolved_link_writer_get_by_name; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->reader_union_branch = -1; + self->calculate_size = avro_resolved_link_writer_calculate_size; + self->free_iface = avro_resolved_link_writer_free_iface; + self->init = avro_resolved_link_writer_init; + self->done = avro_resolved_link_writer_done; + self->reset_wrappers = avro_resolved_link_writer_reset; + + self->parent.get_boolean = avro_resolved_link_writer_get_boolean; + self->parent.get_bytes = avro_resolved_link_writer_get_bytes; + self->parent.grab_bytes = avro_resolved_link_writer_grab_bytes; + self->parent.get_double = avro_resolved_link_writer_get_double; + self->parent.get_float = avro_resolved_link_writer_get_float; + self->parent.get_int = avro_resolved_link_writer_get_int; + self->parent.get_long = avro_resolved_link_writer_get_long; + self->parent.get_null = avro_resolved_link_writer_get_null; + self->parent.get_string = avro_resolved_link_writer_get_string; + self->parent.grab_string = avro_resolved_link_writer_grab_string; + self->parent.get_enum = avro_resolved_link_writer_get_enum; + self->parent.get_fixed = avro_resolved_link_writer_get_fixed; + self->parent.grab_fixed = avro_resolved_link_writer_grab_fixed; + + self->parent.set_boolean = avro_resolved_link_writer_set_boolean; + self->parent.set_bytes = avro_resolved_link_writer_set_bytes; + self->parent.give_bytes = avro_resolved_link_writer_give_bytes; + self->parent.set_double = avro_resolved_link_writer_set_double; + self->parent.set_float = avro_resolved_link_writer_set_float; + self->parent.set_int = avro_resolved_link_writer_set_int; + self->parent.set_long = avro_resolved_link_writer_set_long; + self->parent.set_null = avro_resolved_link_writer_set_null; + self->parent.set_string = avro_resolved_link_writer_set_string; + self->parent.set_string_len = avro_resolved_link_writer_set_string_len; + self->parent.give_string_len = avro_resolved_link_writer_give_string_len; + self->parent.set_enum = avro_resolved_link_writer_set_enum; + self->parent.set_fixed = avro_resolved_link_writer_set_fixed; + self->parent.give_fixed = avro_resolved_link_writer_give_fixed; + + self->parent.get_size = avro_resolved_link_writer_get_size; + self->parent.get_by_index = avro_resolved_link_writer_get_by_index; + self->parent.get_by_name = avro_resolved_link_writer_get_by_name; + self->parent.get_discriminant = avro_resolved_link_writer_get_discriminant; + self->parent.get_current_branch = avro_resolved_link_writer_get_current_branch; + + self->parent.append = avro_resolved_link_writer_append; + self->parent.add = avro_resolved_link_writer_add; + self->parent.set_branch = avro_resolved_link_writer_set_branch; + + return container_of(self, avro_resolved_link_writer_t, parent); +} + +static int +try_link(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + AVRO_UNUSED(rschema); + + /* + * For link schemas, we create a special value implementation + * that allocates space for its wrapped value at runtime. This + * lets us handle recursive types without having to instantiate + * in infinite-size value. + */ + + avro_schema_t wtarget = avro_schema_link_target(wschema); + avro_resolved_link_writer_t *lself = + avro_resolved_link_writer_create(wtarget, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, lself); + + avro_resolved_writer_t *target_resolver = + avro_resolved_writer_new_memoized(state, wtarget, rschema); + if (target_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, root_rschema); + avro_value_iface_decref(&lself->parent.parent); + avro_prefix_error("Link target isn't compatible: "); + DEBUG("%s", avro_strerror()); + return EINVAL; + } + + lself->target_resolver = target_resolver; + lself->next = state->links; + state->links = lself; + + *self = &lself->parent; + return 0; +} + + +/*----------------------------------------------------------------------- + * boolean + */ + +static int +avro_resolved_writer_set_boolean(const avro_value_iface_t *viface, + void *vself, int val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing %s into %p", val? "TRUE": "FALSE", dest.self); + return avro_value_set_boolean(&dest, val); +} + +static int +try_boolean(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_boolean(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_boolean = avro_resolved_writer_set_boolean; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * bytes + */ + +static int +avro_resolved_writer_set_bytes(const avro_value_iface_t *viface, + void *vself, void *buf, size_t size) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing <%p:%" PRIsz "> into %p", buf, size, dest.self); + return avro_value_set_bytes(&dest, buf, size); +} + +static int +avro_resolved_writer_give_bytes(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *buf) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing [%p] into %p", buf, dest.self); + return avro_value_give_bytes(&dest, buf); +} + +static int +try_bytes(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_bytes(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_bytes = avro_resolved_writer_set_bytes; + (*self)->parent.give_bytes = avro_resolved_writer_give_bytes; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * double + */ + +static int +avro_resolved_writer_set_double(const avro_value_iface_t *viface, + void *vself, double val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing %le into %p", val, dest.self); + return avro_value_set_double(&dest, val); +} + +static int +try_double(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_double(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_double = avro_resolved_writer_set_double; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * float + */ + +static int +avro_resolved_writer_set_float(const avro_value_iface_t *viface, + void *vself, float val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing %e into %p", val, dest.self); + return avro_value_set_float(&dest, val); +} + +static int +avro_resolved_writer_set_float_double(const avro_value_iface_t *viface, + void *vself, float val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Promoting float %e into double %p", val, dest.self); + return avro_value_set_double(&dest, val); +} + +static int +try_float(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_float(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_float = avro_resolved_writer_set_float; + } + + else if (is_avro_double(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_float = avro_resolved_writer_set_float_double; + } + + return 0; +} + + +/*----------------------------------------------------------------------- + * int + */ + +static int +avro_resolved_writer_set_int(const avro_value_iface_t *viface, + void *vself, int32_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing %" PRId32 " into %p", val, dest.self); + return avro_value_set_int(&dest, val); +} + +static int +avro_resolved_writer_set_int_double(const avro_value_iface_t *viface, + void *vself, int32_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Promoting int %" PRId32 " into double %p", val, dest.self); + return avro_value_set_double(&dest, val); +} + +static int +avro_resolved_writer_set_int_float(const avro_value_iface_t *viface, + void *vself, int32_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Promoting int %" PRId32 " into float %p", val, dest.self); + return avro_value_set_float(&dest, (float) val); +} + +static int +avro_resolved_writer_set_int_long(const avro_value_iface_t *viface, + void *vself, int32_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Promoting int %" PRId32 " into long %p", val, dest.self); + return avro_value_set_long(&dest, val); +} + +static int +try_int(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_int32(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_int = avro_resolved_writer_set_int; + } + + else if (is_avro_int64(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_int = avro_resolved_writer_set_int_long; + } + + else if (is_avro_double(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_int = avro_resolved_writer_set_int_double; + } + + else if (is_avro_float(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_int = avro_resolved_writer_set_int_float; + } + + return 0; +} + + +/*----------------------------------------------------------------------- + * long + */ + +static int +avro_resolved_writer_set_long(const avro_value_iface_t *viface, + void *vself, int64_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing %" PRId64 " into %p", val, dest.self); + return avro_value_set_long(&dest, val); +} + +static int +avro_resolved_writer_set_long_double(const avro_value_iface_t *viface, + void *vself, int64_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Promoting long %" PRId64 " into double %p", val, dest.self); + return avro_value_set_double(&dest, (double) val); +} + +static int +avro_resolved_writer_set_long_float(const avro_value_iface_t *viface, + void *vself, int64_t val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Promoting long %" PRId64 " into float %p", val, dest.self); + return avro_value_set_float(&dest, (float) val); +} + +static int +try_long(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_int64(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_long = avro_resolved_writer_set_long; + } + + else if (is_avro_double(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_long = avro_resolved_writer_set_long_double; + } + + else if (is_avro_float(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_long = avro_resolved_writer_set_long_float; + } + + return 0; +} + + +/*----------------------------------------------------------------------- + * null + */ + +static int +avro_resolved_writer_set_null(const avro_value_iface_t *viface, + void *vself) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing NULL into %p", dest.self); + return avro_value_set_null(&dest); +} + +static int +try_null(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_null(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_null = avro_resolved_writer_set_null; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * string + */ + +static int +avro_resolved_writer_set_string(const avro_value_iface_t *viface, + void *vself, const char *str) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing \"%s\" into %p", str, dest.self); + return avro_value_set_string(&dest, str); +} + +static int +avro_resolved_writer_set_string_len(const avro_value_iface_t *viface, + void *vself, const char *str, size_t size) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing <%p:%" PRIsz "> into %p", str, size, dest.self); + return avro_value_set_string_len(&dest, str, size); +} + +static int +avro_resolved_writer_give_string_len(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *buf) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing [%p] into %p", buf, dest.self); + return avro_value_give_string_len(&dest, buf); +} + +static int +try_string(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_string(rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_string = avro_resolved_writer_set_string; + (*self)->parent.set_string_len = avro_resolved_writer_set_string_len; + (*self)->parent.give_string_len = avro_resolved_writer_give_string_len; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * array + */ + +typedef struct avro_resolved_array_writer { + avro_resolved_writer_t parent; + avro_resolved_writer_t *child_resolver; +} avro_resolved_array_writer_t; + +typedef struct avro_resolved_array_value { + avro_value_t wrapped; + avro_raw_array_t children; +} avro_resolved_array_value_t; + +static void +avro_resolved_array_writer_calculate_size(avro_resolved_writer_t *iface) +{ + avro_resolved_array_writer_t *aiface = + container_of(iface, avro_resolved_array_writer_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_array_value_t); + + avro_resolved_writer_calculate_size(aiface->child_resolver); +} + +static void +avro_resolved_array_writer_free_iface(avro_resolved_writer_t *iface, st_table *freeing) +{ + avro_resolved_array_writer_t *aiface = + container_of(iface, avro_resolved_array_writer_t, parent); + free_resolver(aiface->child_resolver, freeing); + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_array_writer_t, iface); +} + +static int +avro_resolved_array_writer_init(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_array_writer_t *aiface = + container_of(iface, avro_resolved_array_writer_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + size_t child_instance_size = aiface->child_resolver->instance_size; + DEBUG("Initializing child array (child_size=%" PRIsz ")", child_instance_size); + avro_raw_array_init(&self->children, child_instance_size); + return 0; +} + +static void +avro_resolved_array_writer_free_elements(const avro_resolved_writer_t *child_iface, + avro_resolved_array_value_t *self) +{ + size_t i; + for (i = 0; i < avro_raw_array_size(&self->children); i++) { + void *child_self = avro_raw_array_get_raw(&self->children, i); + avro_resolved_writer_done(child_iface, child_self); + } +} + +static void +avro_resolved_array_writer_done(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_array_writer_t *aiface = + container_of(iface, avro_resolved_array_writer_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + avro_resolved_array_writer_free_elements(aiface->child_resolver, self); + avro_raw_array_done(&self->children); +} + +static int +avro_resolved_array_writer_reset(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_array_writer_t *aiface = + container_of(iface, avro_resolved_array_writer_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + + /* Clear out our cache of wrapped children */ + avro_resolved_array_writer_free_elements(aiface->child_resolver, self); + avro_raw_array_clear(&self->children); + return 0; +} + +static int +avro_resolved_array_writer_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_array_value_t *self = (const avro_resolved_array_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, &self->wrapped, &dest)); + return avro_value_get_size(&dest, size); +} + +static int +avro_resolved_array_writer_append(const avro_value_iface_t *viface, + void *vself, avro_value_t *child_out, + size_t *new_index) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_array_writer_t *aiface = + container_of(iface, avro_resolved_array_writer_t, parent); + avro_resolved_array_value_t *self = (avro_resolved_array_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, &self->wrapped, &dest)); + + child_out->iface = &aiface->child_resolver->parent; + child_out->self = avro_raw_array_append(&self->children); + if (child_out->self == NULL) { + avro_set_error("Couldn't expand array"); + return ENOMEM; + } + + DEBUG("Appending to array %p", dest.self); + check(rval, avro_value_append(&dest, (avro_value_t *) child_out->self, new_index)); + return avro_resolved_writer_init(aiface->child_resolver, child_out->self); +} + +static avro_resolved_array_writer_t * +avro_resolved_array_writer_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_writer_t *self = (avro_resolved_writer_t *) avro_new(avro_resolved_array_writer_t); + memset(self, 0, sizeof(avro_resolved_array_writer_t)); + + self->parent.incref_iface = avro_resolved_writer_incref_iface; + self->parent.decref_iface = avro_resolved_writer_decref_iface; + self->parent.incref = avro_resolved_writer_incref; + self->parent.decref = avro_resolved_writer_decref; + self->parent.reset = avro_resolved_writer_reset; + self->parent.get_type = avro_resolved_writer_get_type; + self->parent.get_schema = avro_resolved_writer_get_schema; + self->parent.get_size = avro_resolved_array_writer_get_size; + self->parent.append = avro_resolved_array_writer_append; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->reader_union_branch = -1; + self->calculate_size = avro_resolved_array_writer_calculate_size; + self->free_iface = avro_resolved_array_writer_free_iface; + self->init = avro_resolved_array_writer_init; + self->done = avro_resolved_array_writer_done; + self->reset_wrappers = avro_resolved_array_writer_reset; + return container_of(self, avro_resolved_array_writer_t, parent); +} + +static int +try_array(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * First verify that the reader is an array. + */ + + if (!is_avro_array(rschema)) { + return 0; + } + + /* + * Array schemas have to have compatible element schemas to be + * compatible themselves. Try to create an resolver to check + * the compatibility. + */ + + avro_resolved_array_writer_t *aself = + avro_resolved_array_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, aself); + + avro_schema_t witems = avro_schema_array_items(wschema); + avro_schema_t ritems = avro_schema_array_items(rschema); + + avro_resolved_writer_t *item_resolver = + avro_resolved_writer_new_memoized(state, witems, ritems); + if (item_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, root_rschema); + avro_value_iface_decref(&aself->parent.parent); + avro_prefix_error("Array values aren't compatible: "); + return EINVAL; + } + + /* + * The two schemas are compatible. Store the item schema's + * resolver into the child_resolver field. + */ + + aself->child_resolver = item_resolver; + *self = &aself->parent; + return 0; +} + + +/*----------------------------------------------------------------------- + * enum + */ + +static int +avro_resolved_writer_set_enum(const avro_value_iface_t *viface, + void *vself, int val) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing %d into %p", val, dest.self); + return avro_value_set_enum(&dest, val); +} + +static int +try_enum(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * Enum schemas have to have the same name — but not the same + * list of symbols — to be compatible. + */ + + if (is_avro_enum(rschema)) { + const char *wname = avro_schema_name(wschema); + const char *rname = avro_schema_name(rschema); + + if (strcmp(wname, rname) == 0) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_enum = avro_resolved_writer_set_enum; + } + } + return 0; +} + + +/*----------------------------------------------------------------------- + * fixed + */ + +static int +avro_resolved_writer_set_fixed(const avro_value_iface_t *viface, + void *vself, void *buf, size_t size) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing <%p:%" PRIsz "> into (fixed) %p", buf, size, dest.self); + return avro_value_set_fixed(&dest, buf, size); +} + +static int +avro_resolved_writer_give_fixed(const avro_value_iface_t *viface, + void *vself, avro_wrapped_buffer_t *buf) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + avro_value_t *self = (avro_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, self, &dest)); + DEBUG("Storing [%p] into (fixed) %p", buf, dest.self); + return avro_value_give_fixed(&dest, buf); +} + +static int +try_fixed(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * Fixed schemas need the same name and size to be compatible. + */ + + if (avro_schema_equal(wschema, rschema)) { + *self = avro_resolved_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, *self); + (*self)->parent.set_fixed = avro_resolved_writer_set_fixed; + (*self)->parent.give_fixed = avro_resolved_writer_give_fixed; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * map + */ + +typedef struct avro_resolved_map_writer { + avro_resolved_writer_t parent; + avro_resolved_writer_t *child_resolver; +} avro_resolved_map_writer_t; + +typedef struct avro_resolved_map_value { + avro_value_t wrapped; + avro_raw_array_t children; +} avro_resolved_map_value_t; + +static void +avro_resolved_map_writer_calculate_size(avro_resolved_writer_t *iface) +{ + avro_resolved_map_writer_t *miface = + container_of(iface, avro_resolved_map_writer_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + iface->instance_size = sizeof(avro_resolved_map_value_t); + + avro_resolved_writer_calculate_size(miface->child_resolver); +} + +static void +avro_resolved_map_writer_free_iface(avro_resolved_writer_t *iface, st_table *freeing) +{ + avro_resolved_map_writer_t *miface = + container_of(iface, avro_resolved_map_writer_t, parent); + free_resolver(miface->child_resolver, freeing); + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_map_writer_t, iface); +} + +static int +avro_resolved_map_writer_init(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_map_writer_t *miface = + container_of(iface, avro_resolved_map_writer_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + size_t child_instance_size = miface->child_resolver->instance_size; + DEBUG("Initializing child array for map (child_size=%" PRIsz ")", child_instance_size); + avro_raw_array_init(&self->children, child_instance_size); + return 0; +} + +static void +avro_resolved_map_writer_free_elements(const avro_resolved_writer_t *child_iface, + avro_resolved_map_value_t *self) +{ + size_t i; + for (i = 0; i < avro_raw_array_size(&self->children); i++) { + void *child_self = avro_raw_array_get_raw(&self->children, i); + avro_resolved_writer_done(child_iface, child_self); + } +} + +static void +avro_resolved_map_writer_done(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_map_writer_t *miface = + container_of(iface, avro_resolved_map_writer_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + avro_resolved_map_writer_free_elements(miface->child_resolver, self); + avro_raw_array_done(&self->children); +} + +static int +avro_resolved_map_writer_reset(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_map_writer_t *miface = + container_of(iface, avro_resolved_map_writer_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + + /* Clear out our cache of wrapped children */ + avro_resolved_map_writer_free_elements(miface->child_resolver, self); + return 0; +} + +static int +avro_resolved_map_writer_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_map_value_t *self = (const avro_resolved_map_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, &self->wrapped, &dest)); + return avro_value_get_size(&dest, size); +} + +static int +avro_resolved_map_writer_add(const avro_value_iface_t *viface, + void *vself, const char *key, + avro_value_t *child, size_t *index, int *is_new) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_map_writer_t *miface = + container_of(iface, avro_resolved_map_writer_t, parent); + avro_resolved_map_value_t *self = (avro_resolved_map_value_t *) vself; + avro_value_t dest; + check(rval, avro_resolved_writer_get_real_dest(iface, &self->wrapped, &dest)); + + /* + * This is a bit convoluted. We need to stash the wrapped child + * value somewhere in our children array. But we don't know + * where to put it until the wrapped map tells us whether this + * is a new value, and if not, which index the value should go + * in. + */ + + avro_value_t real_child; + size_t real_index; + int real_is_new; + + DEBUG("Adding %s to map %p", key, dest.self); + check(rval, avro_value_add(&dest, key, &real_child, &real_index, &real_is_new)); + + child->iface = &miface->child_resolver->parent; + if (real_is_new) { + child->self = avro_raw_array_append(&self->children); + DEBUG("Element is new (child resolver=%p)", child->self); + if (child->self == NULL) { + avro_set_error("Couldn't expand map"); + return ENOMEM; + } + check(rval, avro_resolved_writer_init + (miface->child_resolver, child->self)); + } else { + child->self = avro_raw_array_get_raw(&self->children, real_index); + DEBUG("Element is old (child resolver=%p)", child->self); + } + avro_value_t *child_vself = (avro_value_t *) child->self; + *child_vself = real_child; + + if (index != NULL) { + *index = real_index; + } + if (is_new != NULL) { + *is_new = real_is_new; + } + return 0; +} + +static avro_resolved_map_writer_t * +avro_resolved_map_writer_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_writer_t *self = (avro_resolved_writer_t *) avro_new(avro_resolved_map_writer_t); + memset(self, 0, sizeof(avro_resolved_map_writer_t)); + + self->parent.incref_iface = avro_resolved_writer_incref_iface; + self->parent.decref_iface = avro_resolved_writer_decref_iface; + self->parent.incref = avro_resolved_writer_incref; + self->parent.decref = avro_resolved_writer_decref; + self->parent.reset = avro_resolved_writer_reset; + self->parent.get_type = avro_resolved_writer_get_type; + self->parent.get_schema = avro_resolved_writer_get_schema; + self->parent.get_size = avro_resolved_map_writer_get_size; + self->parent.add = avro_resolved_map_writer_add; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->reader_union_branch = -1; + self->calculate_size = avro_resolved_map_writer_calculate_size; + self->free_iface = avro_resolved_map_writer_free_iface; + self->init = avro_resolved_map_writer_init; + self->done = avro_resolved_map_writer_done; + self->reset_wrappers = avro_resolved_map_writer_reset; + return container_of(self, avro_resolved_map_writer_t, parent); +} + +static int +try_map(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * First verify that the reader is an map. + */ + + if (!is_avro_map(rschema)) { + return 0; + } + + /* + * Map schemas have to have compatible element schemas to be + * compatible themselves. Try to create an resolver to check + * the compatibility. + */ + + avro_resolved_map_writer_t *mself = + avro_resolved_map_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, mself); + + avro_schema_t witems = avro_schema_map_values(wschema); + avro_schema_t ritems = avro_schema_map_values(rschema); + + avro_resolved_writer_t *item_resolver = + avro_resolved_writer_new_memoized(state, witems, ritems); + if (item_resolver == NULL) { + avro_memoize_delete(&state->mem, wschema, root_rschema); + avro_value_iface_decref(&mself->parent.parent); + avro_prefix_error("Map values aren't compatible: "); + return EINVAL; + } + + /* + * The two schemas are compatible. Store the item schema's + * resolver into the child_resolver field. + */ + + mself->child_resolver = item_resolver; + *self = &mself->parent; + return 0; +} + + +/*----------------------------------------------------------------------- + * record + */ + +typedef struct avro_resolved_record_writer { + avro_resolved_writer_t parent; + size_t field_count; + size_t *field_offsets; + avro_resolved_writer_t **field_resolvers; + size_t *index_mapping; +} avro_resolved_record_writer_t; + +typedef struct avro_resolved_record_value { + avro_value_t wrapped; + /* The rest of the struct is taken up by the inline storage + * needed for each field. */ +} avro_resolved_record_value_t; + +/** Return a pointer to the given field within a record struct. */ +#define avro_resolved_record_field(iface, rec, index) \ + (((char *) (rec)) + (iface)->field_offsets[(index)]) + + +static void +avro_resolved_record_writer_calculate_size(avro_resolved_writer_t *iface) +{ + avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + + /* + * Once we've figured out which writer fields we actually need, + * calculate an offset for each one. + */ + + size_t wi; + size_t next_offset = sizeof(avro_resolved_record_value_t); + for (wi = 0; wi < riface->field_count; wi++) { + riface->field_offsets[wi] = next_offset; + if (riface->field_resolvers[wi] != NULL) { + avro_resolved_writer_calculate_size + (riface->field_resolvers[wi]); + size_t field_size = + riface->field_resolvers[wi]->instance_size; + DEBUG("Field %" PRIsz " has size %" PRIsz, wi, field_size); + next_offset += field_size; + } else { + DEBUG("Field %" PRIsz " is being skipped", wi); + } + } + + DEBUG("Record has size %" PRIsz, next_offset); + iface->instance_size = next_offset; +} + +static void +avro_resolved_record_writer_free_iface(avro_resolved_writer_t *iface, st_table *freeing) +{ + avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + + if (riface->field_offsets != NULL) { + avro_free(riface->field_offsets, + riface->field_count * sizeof(size_t)); + } + + if (riface->field_resolvers != NULL) { + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + DEBUG("Freeing field %" PRIsz " %p", i, + riface->field_resolvers[i]); + free_resolver(riface->field_resolvers[i], freeing); + } + } + avro_free(riface->field_resolvers, + riface->field_count * sizeof(avro_resolved_writer_t *)); + } + + if (riface->index_mapping != NULL) { + avro_free(riface->index_mapping, + riface->field_count * sizeof(size_t)); + } + + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_record_writer_t, iface); +} + +static int +avro_resolved_record_writer_init(const avro_resolved_writer_t *iface, void *vself) +{ + int rval; + const avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + /* Initialize each field */ + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + check(rval, avro_resolved_writer_init + (riface->field_resolvers[i], + avro_resolved_record_field(riface, self, i))); + } + } + + return 0; +} + +static void +avro_resolved_record_writer_done(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + /* Finalize each field */ + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + avro_resolved_writer_done + (riface->field_resolvers[i], + avro_resolved_record_field(riface, self, i)); + } + } +} + +static int +avro_resolved_record_writer_reset(const avro_resolved_writer_t *iface, void *vself) +{ + int rval; + const avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + avro_resolved_record_value_t *self = (avro_resolved_record_value_t *) vself; + + /* Reset each field */ + size_t i; + for (i = 0; i < riface->field_count; i++) { + if (riface->field_resolvers[i] != NULL) { + check(rval, avro_resolved_writer_reset_wrappers + (riface->field_resolvers[i], + avro_resolved_record_field(riface, self, i))); + } + } + + return 0; +} + +static int +avro_resolved_record_writer_get_size(const avro_value_iface_t *viface, + const void *vself, size_t *size) +{ + AVRO_UNUSED(vself); + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + *size = riface->field_count; + return 0; +} + +static int +avro_resolved_record_writer_get_by_index(const avro_value_iface_t *viface, + const void *vself, size_t index, + avro_value_t *child, const char **name) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_record_writer_t *riface = + container_of(iface, avro_resolved_record_writer_t, parent); + const avro_resolved_record_value_t *self = (const avro_resolved_record_value_t *) vself; + avro_value_t dest; + + DEBUG("Getting writer field %" PRIsz " from record %p", index, self); + if (riface->field_resolvers[index] == NULL) { + DEBUG("Reader doesn't have field, skipping"); + child->iface = NULL; + child->self = NULL; + return 0; + } + + check(rval, avro_resolved_writer_get_real_dest(iface, &self->wrapped, &dest)); + size_t reader_index = riface->index_mapping[index]; + DEBUG(" Reader field is %" PRIsz, reader_index); + child->iface = &riface->field_resolvers[index]->parent; + child->self = avro_resolved_record_field(riface, self, index); + + return avro_value_get_by_index(&dest, reader_index, (avro_value_t *) child->self, name); +} + +static int +avro_resolved_record_writer_get_by_name(const avro_value_iface_t *viface, + const void *vself, const char *name, + avro_value_t *child, size_t *index) +{ + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + + int wi = avro_schema_record_field_get_index(iface->wschema, name); + if (wi == -1) { + avro_set_error("Record doesn't have field named %s", name); + return EINVAL; + } + + DEBUG("Writer field %s is at index %d", name, wi); + if (index != NULL) { + *index = wi; + } + return avro_resolved_record_writer_get_by_index(viface, vself, wi, child, NULL); +} + +static avro_resolved_record_writer_t * +avro_resolved_record_writer_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_writer_t *self = (avro_resolved_writer_t *) avro_new(avro_resolved_record_writer_t); + memset(self, 0, sizeof(avro_resolved_record_writer_t)); + + self->parent.incref_iface = avro_resolved_writer_incref_iface; + self->parent.decref_iface = avro_resolved_writer_decref_iface; + self->parent.incref = avro_resolved_writer_incref; + self->parent.decref = avro_resolved_writer_decref; + self->parent.reset = avro_resolved_writer_reset; + self->parent.get_type = avro_resolved_writer_get_type; + self->parent.get_schema = avro_resolved_writer_get_schema; + self->parent.get_size = avro_resolved_record_writer_get_size; + self->parent.get_by_index = avro_resolved_record_writer_get_by_index; + self->parent.get_by_name = avro_resolved_record_writer_get_by_name; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->reader_union_branch = -1; + self->calculate_size = avro_resolved_record_writer_calculate_size; + self->free_iface = avro_resolved_record_writer_free_iface; + self->init = avro_resolved_record_writer_init; + self->done = avro_resolved_record_writer_done; + self->reset_wrappers = avro_resolved_record_writer_reset; + return container_of(self, avro_resolved_record_writer_t, parent); +} + +static int +try_record(memoize_state_t *state, avro_resolved_writer_t **self, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * First verify that the reader is also a record, and has the + * same name as the writer. + */ + + if (!is_avro_record(rschema)) { + return 0; + } + + const char *wname = avro_schema_name(wschema); + const char *rname = avro_schema_name(rschema); + + if (strcmp(wname, rname) != 0) { + return 0; + } + + /* + * Categorize the fields in the record schemas. Fields that are + * only in the writer are ignored. Fields that are only in the + * reader raise a schema mismatch error, unless the field has a + * default value. Fields that are in both are resolved + * recursively. + * + * The field_resolvers array will contain an avro_value_iface_t + * for each field in the writer schema. To build this array, we + * loop through the fields of the reader schema. If that field + * is also in the writer schema, we resolve them recursively, + * and store the resolver into the array. If the field isn't in + * the writer schema, we raise an error. (TODO: Eventually, + * we'll handle default values here.) After this loop finishes, + * any NULLs in the field_resolvers array will represent fields + * in the writer but not the reader; these fields will be + * skipped when processing the input. + */ + + avro_resolved_record_writer_t *rself = + avro_resolved_record_writer_create(wschema, root_rschema); + avro_memoize_set(&state->mem, wschema, root_rschema, rself); + + size_t wfields = avro_schema_record_size(wschema); + size_t rfields = avro_schema_record_size(rschema); + + DEBUG("Checking writer record schema %s", wname); + + avro_resolved_writer_t **field_resolvers = + (avro_resolved_writer_t **) avro_calloc(wfields, sizeof(avro_resolved_writer_t *)); + size_t *field_offsets = (size_t *) avro_calloc(wfields, sizeof(size_t)); + size_t *index_mapping = (size_t *) avro_calloc(wfields, sizeof(size_t)); + + size_t ri; + for (ri = 0; ri < rfields; ri++) { + avro_schema_t rfield = + avro_schema_record_field_get_by_index(rschema, ri); + const char *field_name = + avro_schema_record_field_name(rschema, ri); + + DEBUG("Resolving reader record field %" PRIsz " (%s)", ri, field_name); + + /* + * See if this field is also in the writer schema. + */ + + int wi = avro_schema_record_field_get_index(wschema, field_name); + + if (wi == -1) { + /* + * This field isn't in the writer schema — + * that's an error! TODO: Handle default + * values! + */ + + DEBUG("Field %s isn't in writer", field_name); + + /* Allow missing fields in the writer. They + * will default to zero. So skip over the + * missing field, and continue building the + * resolver. Note also that all missing values + * are zero because avro_generic_value_new() + * initializes all values of the reader to 0 + * on creation. This is a work-around because + * default values are not implemented yet. + */ + #ifdef AVRO_ALLOW_MISSING_FIELDS_IN_RESOLVED_WRITER + continue; + #else + avro_set_error("Reader field %s doesn't appear in writer", + field_name); + goto error; + #endif + } + + /* + * Try to recursively resolve the schemas for this + * field. If they're not compatible, that's an error. + */ + + avro_schema_t wfield = + avro_schema_record_field_get_by_index(wschema, wi); + avro_resolved_writer_t *field_resolver = + avro_resolved_writer_new_memoized(state, wfield, rfield); + + if (field_resolver == NULL) { + avro_prefix_error("Field %s isn't compatible: ", field_name); + goto error; + } + + /* + * Save the details for this field. + */ + + DEBUG("Found match for field %s (%" PRIsz " in reader, %d in writer)", + field_name, ri, wi); + field_resolvers[wi] = field_resolver; + index_mapping[wi] = ri; + } + + /* + * We might not have found matches for all of the writer fields, + * but that's okay — any extras will be ignored. + */ + + rself->field_count = wfields; + rself->field_offsets = field_offsets; + rself->field_resolvers = field_resolvers; + rself->index_mapping = index_mapping; + *self = &rself->parent; + return 0; + +error: + /* + * Clean up any resolver we might have already created. + */ + + avro_memoize_delete(&state->mem, wschema, root_rschema); + avro_value_iface_decref(&rself->parent.parent); + + { + unsigned int i; + for (i = 0; i < wfields; i++) { + if (field_resolvers[i]) { + avro_value_iface_decref(&field_resolvers[i]->parent); + } + } + } + + avro_free(field_resolvers, wfields * sizeof(avro_resolved_writer_t *)); + avro_free(field_offsets, wfields * sizeof(size_t)); + avro_free(index_mapping, wfields * sizeof(size_t)); + return EINVAL; +} + + +/*----------------------------------------------------------------------- + * union + */ + +typedef struct avro_resolved_union_writer { + avro_resolved_writer_t parent; + size_t branch_count; + avro_resolved_writer_t **branch_resolvers; +} avro_resolved_union_writer_t; + +typedef struct avro_resolved_union_value { + avro_value_t wrapped; + + /** The currently active branch of the union. -1 if no branch + * is selected. */ + int discriminant; + + /* The rest of the struct is taken up by the inline storage + * needed for the active branch. */ +} avro_resolved_union_value_t; + +/** Return a pointer to the active branch within a union struct. */ +#define avro_resolved_union_branch(_union) \ + (((char *) (_union)) + sizeof(avro_resolved_union_value_t)) + + +static void +avro_resolved_union_writer_calculate_size(avro_resolved_writer_t *iface) +{ + avro_resolved_union_writer_t *uiface = + container_of(iface, avro_resolved_union_writer_t, parent); + + /* Only calculate the size for any resolver once */ + iface->calculate_size = NULL; + + DEBUG("Calculating size for %s->%s", + avro_schema_type_name((iface)->wschema), + avro_schema_type_name((iface)->rschema)); + + size_t i; + size_t max_branch_size = 0; + for (i = 0; i < uiface->branch_count; i++) { + if (uiface->branch_resolvers[i] == NULL) { + DEBUG("No match for writer union branch %" PRIsz, i); + } else { + avro_resolved_writer_calculate_size + (uiface->branch_resolvers[i]); + size_t branch_size = + uiface->branch_resolvers[i]->instance_size; + DEBUG("Writer branch %" PRIsz " has size %" PRIsz, i, branch_size); + if (branch_size > max_branch_size) { + max_branch_size = branch_size; + } + } + } + + DEBUG("Maximum branch size is %" PRIsz, max_branch_size); + iface->instance_size = + sizeof(avro_resolved_union_value_t) + max_branch_size; + DEBUG("Total union size is %" PRIsz, iface->instance_size); +} + +static void +avro_resolved_union_writer_free_iface(avro_resolved_writer_t *iface, st_table *freeing) +{ + avro_resolved_union_writer_t *uiface = + container_of(iface, avro_resolved_union_writer_t, parent); + + if (uiface->branch_resolvers != NULL) { + size_t i; + for (i = 0; i < uiface->branch_count; i++) { + if (uiface->branch_resolvers[i] != NULL) { + free_resolver(uiface->branch_resolvers[i], freeing); + } + } + avro_free(uiface->branch_resolvers, + uiface->branch_count * sizeof(avro_resolved_writer_t *)); + } + + avro_schema_decref(iface->wschema); + avro_schema_decref(iface->rschema); + avro_freet(avro_resolved_union_writer_t, iface); +} + +static int +avro_resolved_union_writer_init(const avro_resolved_writer_t *iface, void *vself) +{ + AVRO_UNUSED(iface); + avro_resolved_union_value_t *self = (avro_resolved_union_value_t *) vself; + self->discriminant = -1; + return 0; +} + +static void +avro_resolved_union_writer_done(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_union_writer_t *uiface = + container_of(iface, avro_resolved_union_writer_t, parent); + avro_resolved_union_value_t *self = (avro_resolved_union_value_t *) vself; + if (self->discriminant >= 0) { + avro_resolved_writer_done + (uiface->branch_resolvers[self->discriminant], + avro_resolved_union_branch(self)); + self->discriminant = -1; + } +} + +static int +avro_resolved_union_writer_reset(const avro_resolved_writer_t *iface, void *vself) +{ + const avro_resolved_union_writer_t *uiface = + container_of(iface, avro_resolved_union_writer_t, parent); + avro_resolved_union_value_t *self = (avro_resolved_union_value_t *) vself; + + /* Keep the same branch selected, for the common case that we're + * about to reuse it. */ + if (self->discriminant >= 0) { + return avro_resolved_writer_reset_wrappers + (uiface->branch_resolvers[self->discriminant], + avro_resolved_union_branch(self)); + } + + return 0; +} + +static int +avro_resolved_union_writer_set_branch(const avro_value_iface_t *viface, + void *vself, int discriminant, + avro_value_t *branch) +{ + int rval; + const avro_resolved_writer_t *iface = + container_of(viface, avro_resolved_writer_t, parent); + const avro_resolved_union_writer_t *uiface = + container_of(iface, avro_resolved_union_writer_t, parent); + avro_resolved_union_value_t *self = (avro_resolved_union_value_t *) vself; + + DEBUG("Getting writer branch %d from union %p", discriminant, vself); + avro_resolved_writer_t *branch_resolver = + uiface->branch_resolvers[discriminant]; + if (branch_resolver == NULL) { + DEBUG("Reader doesn't have branch, skipping"); + avro_set_error("Writer union branch %d is incompatible " + "with reader schema \"%s\"", + discriminant, avro_schema_type_name(iface->rschema)); + return EINVAL; + } + + if (self->discriminant == discriminant) { + DEBUG("Writer branch %d already selected", discriminant); + } else { + if (self->discriminant >= 0) { + DEBUG("Finalizing old writer branch %d", self->discriminant); + avro_resolved_writer_done + (uiface->branch_resolvers[self->discriminant], + avro_resolved_union_branch(self)); + } + DEBUG("Initializing writer branch %d", discriminant); + check(rval, avro_resolved_writer_init + (uiface->branch_resolvers[discriminant], + avro_resolved_union_branch(self))); + self->discriminant = discriminant; + } + + branch->iface = &branch_resolver->parent; + branch->self = avro_resolved_union_branch(self); + avro_value_t *branch_vself = (avro_value_t *) branch->self; + *branch_vself = self->wrapped; + return 0; +} + +static avro_resolved_union_writer_t * +avro_resolved_union_writer_create(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_resolved_writer_t *self = (avro_resolved_writer_t *) avro_new(avro_resolved_union_writer_t); + memset(self, 0, sizeof(avro_resolved_union_writer_t)); + + self->parent.incref_iface = avro_resolved_writer_incref_iface; + self->parent.decref_iface = avro_resolved_writer_decref_iface; + self->parent.incref = avro_resolved_writer_incref; + self->parent.decref = avro_resolved_writer_decref; + self->parent.reset = avro_resolved_writer_reset; + self->parent.get_type = avro_resolved_writer_get_type; + self->parent.get_schema = avro_resolved_writer_get_schema; + self->parent.set_branch = avro_resolved_union_writer_set_branch; + + self->refcount = 1; + self->wschema = avro_schema_incref(wschema); + self->rschema = avro_schema_incref(rschema); + self->reader_union_branch = -1; + self->calculate_size = avro_resolved_union_writer_calculate_size; + self->free_iface = avro_resolved_union_writer_free_iface; + self->init = avro_resolved_union_writer_init; + self->done = avro_resolved_union_writer_done; + self->reset_wrappers = avro_resolved_union_writer_reset; + return container_of(self, avro_resolved_union_writer_t, parent); +} + +static avro_resolved_writer_t * +try_union(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * For a writer union, we recursively try to resolve each branch + * against the reader schema. This will work correctly whether + * or not the reader is also a union — if the reader is a union, + * then we'll resolve each (non-union) writer branch against the + * reader union, which will be checked in our calls to + * check_simple_writer below. The net result is that we might + * end up trying every combination of writer and reader + * branches, when looking for compatible schemas. + * + * Regardless of what the reader schema is, for each writer + * branch, we stash away the recursive resolver into the + * branch_resolvers array. A NULL entry in this array means + * that that branch isn't compatible with the reader. This + * isn't an immediate schema resolution error, since we allow + * incompatible branches in the types as long as that branch + * never appears in the actual data. We only return an error if + * there are *no* branches that are compatible. + */ + + size_t branch_count = avro_schema_union_size(wschema); + DEBUG("Checking %" PRIsz "-branch writer union schema", branch_count); + + avro_resolved_union_writer_t *uself = + avro_resolved_union_writer_create(wschema, rschema); + avro_memoize_set(&state->mem, wschema, rschema, uself); + + avro_resolved_writer_t **branch_resolvers = + (avro_resolved_writer_t **) avro_calloc(branch_count, sizeof(avro_resolved_writer_t *)); + int some_branch_compatible = 0; + + size_t i; + for (i = 0; i < branch_count; i++) { + avro_schema_t branch_schema = + avro_schema_union_branch(wschema, i); + + DEBUG("Resolving writer union branch %" PRIsz " (%s)", i, + avro_schema_type_name(branch_schema)); + + /* + * Try to recursively resolve this branch of the writer + * union. Don't raise an error if this fails — it's + * okay for some of the branches to not be compatible + * with the reader, as long as those branches never + * appear in the input. + */ + + branch_resolvers[i] = + avro_resolved_writer_new_memoized(state, branch_schema, rschema); + if (branch_resolvers[i] == NULL) { + DEBUG("No match for writer union branch %" PRIsz, i); + } else { + DEBUG("Found match for writer union branch %" PRIsz, i); + some_branch_compatible = 1; + } + } + + /* + * As long as there's at least one branch that's compatible with + * the reader, then we consider this schema resolution a + * success. + */ + + if (!some_branch_compatible) { + DEBUG("No writer union branches match"); + avro_set_error("No branches in the writer are compatible " + "with reader schema %s", + avro_schema_type_name(rschema)); + goto error; + } + + uself->branch_count = branch_count; + uself->branch_resolvers = branch_resolvers; + return &uself->parent; + +error: + /* + * Clean up any resolver we might have already created. + */ + + avro_memoize_delete(&state->mem, wschema, rschema); + avro_value_iface_decref(&uself->parent.parent); + + { + unsigned int i; + for (i = 0; i < branch_count; i++) { + if (branch_resolvers[i]) { + avro_value_iface_decref(&branch_resolvers[i]->parent); + } + } + } + + avro_free(branch_resolvers, branch_count * sizeof(avro_resolved_writer_t *)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * Schema type dispatcher + */ + +static avro_resolved_writer_t * +avro_resolved_writer_new_memoized(memoize_state_t *state, + avro_schema_t wschema, avro_schema_t rschema) +{ + check_param(NULL, is_avro_schema(wschema), "writer schema"); + check_param(NULL, is_avro_schema(rschema), "reader schema"); + + skip_links(rschema); + + /* + * First see if we've already matched these two schemas. If so, + * just return that resolver. + */ + + avro_resolved_writer_t *saved = NULL; + if (avro_memoize_get(&state->mem, wschema, rschema, (void **) &saved)) { + DEBUG("Already resolved %s%s%s->%s", + is_avro_link(wschema)? "[": "", + avro_schema_type_name(wschema), + is_avro_link(wschema)? "]": "", + avro_schema_type_name(rschema)); + avro_value_iface_incref(&saved->parent); + return saved; + } else { + DEBUG("Resolving %s%s%s->%s", + is_avro_link(wschema)? "[": "", + avro_schema_type_name(wschema), + is_avro_link(wschema)? "]": "", + avro_schema_type_name(rschema)); + } + + /* + * Otherwise we have some work to do. + */ + + switch (avro_typeof(wschema)) + { + case AVRO_BOOLEAN: + check_simple_writer(state, wschema, rschema, boolean); + return NULL; + + case AVRO_BYTES: + check_simple_writer(state, wschema, rschema, bytes); + return NULL; + + case AVRO_DOUBLE: + check_simple_writer(state, wschema, rschema, double); + return NULL; + + case AVRO_FLOAT: + check_simple_writer(state, wschema, rschema, float); + return NULL; + + case AVRO_INT32: + check_simple_writer(state, wschema, rschema, int); + return NULL; + + case AVRO_INT64: + check_simple_writer(state, wschema, rschema, long); + return NULL; + + case AVRO_NULL: + check_simple_writer(state, wschema, rschema, null); + return NULL; + + case AVRO_STRING: + check_simple_writer(state, wschema, rschema, string); + return NULL; + + case AVRO_ARRAY: + check_simple_writer(state, wschema, rschema, array); + return NULL; + + case AVRO_ENUM: + check_simple_writer(state, wschema, rschema, enum); + return NULL; + + case AVRO_FIXED: + check_simple_writer(state, wschema, rschema, fixed); + return NULL; + + case AVRO_MAP: + check_simple_writer(state, wschema, rschema, map); + return NULL; + + case AVRO_RECORD: + check_simple_writer(state, wschema, rschema, record); + return NULL; + + case AVRO_UNION: + return try_union(state, wschema, rschema); + + case AVRO_LINK: + check_simple_writer(state, wschema, rschema, link); + return NULL; + + default: + avro_set_error("Unknown schema type"); + return NULL; + } + + return NULL; +} + + +avro_value_iface_t * +avro_resolved_writer_new(avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * Create a state to keep track of the value implementations + * that we create for each subschema. + */ + + memoize_state_t state; + avro_memoize_init(&state.mem); + state.links = NULL; + + /* + * Create the value implementations. + */ + + avro_resolved_writer_t *result = + avro_resolved_writer_new_memoized(&state, wschema, rschema); + if (result == NULL) { + avro_memoize_done(&state.mem); + return NULL; + } + + /* + * Fix up any link schemas so that their value implementations + * point to their target schemas' implementations. + */ + + avro_resolved_writer_calculate_size(result); + while (state.links != NULL) { + avro_resolved_link_writer_t *liface = state.links; + avro_resolved_writer_calculate_size(liface->target_resolver); + state.links = liface->next; + liface->next = NULL; + } + + /* + * And now we can return. + */ + + avro_memoize_done(&state.mem); + return &result->parent; +} diff --git a/fluent-bit/lib/avro/src/resolver.c b/fluent-bit/lib/avro/src/resolver.c new file mode 100644 index 000000000..f0256c265 --- /dev/null +++ b/fluent-bit/lib/avro/src/resolver.c @@ -0,0 +1,1338 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/consumer.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro/legacy.h" +#include "avro/schema.h" +#include "avro_private.h" +#include "st.h" + + +#if !defined(DEBUG_RESOLVER) +#define DEBUG_RESOLVER 0 +#endif + +#if DEBUG_RESOLVER +#include <stdio.h> +#define debug(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } +#else +#define debug(...) /* no debug output */ +#endif + + +typedef struct avro_resolver_t avro_resolver_t; + +struct avro_resolver_t { + avro_consumer_t parent; + + /* The reader schema for this resolver. */ + avro_schema_t rschema; + + /* An array of any child resolvers needed for the subschemas of + * wschema */ + avro_consumer_t **child_resolvers; + + /* If the reader and writer schemas are records, this field + * contains a mapping from writer field indices to reader field + * indices. */ + int *index_mapping; + + /* The number of elements in the child_resolvers and + * index_mapping arrays. */ + size_t num_children; + + /* If the reader schema is a union, but the writer schema is + * not, this field indicates which branch of the reader union + * should be selected. */ + int reader_union_branch; +}; + + +/** + * Frees a resolver object, while ensuring that all of the resolvers in + * a graph of resolvers is only freed once. + */ + +static void +avro_resolver_free_cycles(avro_consumer_t *consumer, st_table *freeing) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + + /* + * First check if we've already started freeing this resolver. + */ + + if (st_lookup(freeing, (st_data_t) resolver, NULL)) { + return; + } + + /* + * Otherwise add this resolver to the freeing set, and then + * actually free the thing. + */ + + st_insert(freeing, (st_data_t) resolver, (st_data_t) NULL); + + avro_schema_decref(resolver->parent.schema); + avro_schema_decref(resolver->rschema); + if (resolver->child_resolvers) { + unsigned int i; + for (i = 0; i < resolver->num_children; i++) { + avro_consumer_t *child = resolver->child_resolvers[i]; + if (child) { + avro_resolver_free_cycles(child, freeing); + } + } + avro_free(resolver->child_resolvers, + sizeof(avro_resolver_t *) * resolver->num_children); + } + if (resolver->index_mapping) { + avro_free(resolver->index_mapping, + sizeof(int) * resolver->num_children); + } + avro_freet(avro_resolver_t, resolver); +} + + +static void +avro_resolver_free(avro_consumer_t *consumer) +{ + st_table *freeing = st_init_numtable(); + avro_resolver_free_cycles(consumer, freeing); + st_free_table(freeing); +} + +/** + * Create a new avro_resolver_t instance. You must fill in the callback + * pointers that are appropriate for the writer schema after this + * function returns. + */ + +static avro_resolver_t * +avro_resolver_create(avro_schema_t wschema, + avro_schema_t rschema) +{ + avro_resolver_t *resolver = (avro_resolver_t *) avro_new(avro_resolver_t); + memset(resolver, 0, sizeof(avro_resolver_t)); + + resolver->parent.free = avro_resolver_free; + resolver->parent.schema = avro_schema_incref(wschema); + resolver->rschema = avro_schema_incref(rschema); + resolver->reader_union_branch = -1; + return resolver; +} + + +static avro_datum_t +avro_resolver_get_real_dest(avro_resolver_t *resolver, avro_datum_t dest) +{ + if (resolver->reader_union_branch < 0) { + /* + * The reader schema isn't a union, so use the dest + * field as-is. + */ + + return dest; + } + + debug("Retrieving union branch %d for %s value", + resolver->reader_union_branch, + avro_schema_type_name(resolver->parent.schema)); + + avro_datum_t branch = NULL; + avro_union_set_discriminant + (dest, resolver->reader_union_branch, &branch); + return branch; +} + + +#define skip_links(schema) \ + while (is_avro_link(schema)) { \ + schema = avro_schema_link_target(schema); \ + } + + +/*----------------------------------------------------------------------- + * Memoized resolvers + */ + +static avro_consumer_t * +avro_resolver_new_memoized(avro_memoize_t *mem, + avro_schema_t wschema, avro_schema_t rschema); + + +/*----------------------------------------------------------------------- + * Reader unions + */ + +/* + * For each Avro type, we have to check whether the reader schema on its + * own is compatible, and whether the reader is a union that contains a + * compatible type. The macros in this section help us perform both of + * these checks with less code. + */ + + +/** + * A helper macro that handles the case where neither writer nor reader + * are unions. Uses @ref check_func to see if the two schemas are + * compatible. + */ + +#define check_non_union(saved, wschema, rschema, check_func) \ +do { \ + avro_resolver_t *self = NULL; \ + int rc = check_func(saved, &self, wschema, rschema, \ + rschema); \ + if (self) { \ + debug("Non-union schemas %s (writer) " \ + "and %s (reader) match", \ + avro_schema_type_name(wschema), \ + avro_schema_type_name(rschema)); \ + \ + self->reader_union_branch = -1; \ + return &self->parent; \ + } \ + \ + if (rc) { \ + return NULL; \ + } \ +} while (0) + + +/** + * Helper macro that handles the case where the reader is a union, and + * the writer is not. Checks each branch of the reader union schema, + * looking for the first branch that is compatible with the writer + * schema. The @ref check_func argument should be a function that can + * check the compatiblity of each branch schema. + */ + +#define check_reader_union(saved, wschema, rschema, check_func) \ +do { \ + if (!is_avro_union(rschema)) { \ + break; \ + } \ + \ + debug("Checking reader union schema"); \ + size_t num_branches = avro_schema_union_size(rschema); \ + unsigned int i; \ + \ + for (i = 0; i < num_branches; i++) { \ + avro_schema_t branch_schema = \ + avro_schema_union_branch(rschema, i); \ + skip_links(branch_schema); \ + avro_resolver_t *self = NULL; \ + int rc = check_func(saved, &self, \ + wschema, branch_schema, \ + rschema); \ + if (self) { \ + debug("Reader union branch %d (%s) " \ + "and writer %s match", \ + i, avro_schema_type_name(branch_schema), \ + avro_schema_type_name(wschema)); \ + self->reader_union_branch = i; \ + return &self->parent; \ + } else { \ + debug("Reader union branch %d (%s) " \ + "doesn't match", \ + i, avro_schema_type_name(branch_schema)); \ + } \ + \ + if (rc) { \ + return NULL; \ + } \ + } \ + \ + debug("No reader union branches match"); \ +} while (0) + +/** + * A helper macro that defines wraps together check_non_union and + * check_reader_union for a simple (non-union) writer schema type. + */ + +#define check_simple_writer(saved, wschema, rschema, type_name) \ +do { \ + check_non_union(saved, wschema, rschema, try_##type_name); \ + check_reader_union(saved, wschema, rschema, try_##type_name); \ + debug("Writer %s doesn't match reader %s", \ + avro_schema_type_name(wschema), \ + avro_schema_type_name(rschema)); \ + avro_set_error("Cannot store " #type_name " into %s", \ + avro_schema_type_name(rschema)); \ + return NULL; \ +} while (0) + + +/*----------------------------------------------------------------------- + * primitives + */ + +static int +avro_resolver_boolean_value(avro_consumer_t *consumer, int value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %s into %p", value? "TRUE": "FALSE", dest); + return avro_boolean_set(dest, value); +} + +static int +try_boolean(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_boolean(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.boolean_value = avro_resolver_boolean_value; + } + return 0; +} + + +static void +free_bytes(void *ptr, size_t sz) +{ + /* + * The binary encoder class allocates bytes values with an extra + * byte, so that they're NUL terminated. + */ + avro_free(ptr, sz+1); +} + +static int +avro_resolver_bytes_value(avro_consumer_t *consumer, + const void *value, size_t value_len, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRIsz " bytes into %p", value_len, dest); + return avro_givebytes_set(dest, (const char *) value, value_len, free_bytes); +} + +static int +try_bytes(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_bytes(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.bytes_value = avro_resolver_bytes_value; + } + return 0; +} + + +static int +avro_resolver_double_value(avro_consumer_t *consumer, double value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %le into %p", value, dest); + return avro_double_set(dest, value); +} + +static int +try_double(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_double(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.double_value = avro_resolver_double_value; + } + return 0; +} + + +static int +avro_resolver_float_value(avro_consumer_t *consumer, float value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %e into %p", value, dest); + return avro_float_set(dest, value); +} + +static int +avro_resolver_float_double_value(avro_consumer_t *consumer, float value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %e into %p (promoting float to double)", value, dest); + return avro_double_set(dest, value); +} + +static int +try_float(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_float(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.float_value = avro_resolver_float_value; + } + else if (is_avro_double(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.float_value = avro_resolver_float_double_value; + } + return 0; +} + + +static int +avro_resolver_int_value(avro_consumer_t *consumer, int32_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId32 " into %p", value, dest); + return avro_int32_set(dest, value); +} + +static int +avro_resolver_int_long_value(avro_consumer_t *consumer, int32_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId32 " into %p (promoting int to long)", value, dest); + return avro_int64_set(dest, value); +} + +static int +avro_resolver_int_double_value(avro_consumer_t *consumer, int32_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId32 " into %p (promoting int to double)", value, dest); + return avro_double_set(dest, value); +} + +static int +avro_resolver_int_float_value(avro_consumer_t *consumer, int32_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId32 " into %p (promoting int to float)", value, dest); + return avro_float_set(dest, (const float) value); +} + +static int +try_int(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_int32(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.int_value = avro_resolver_int_value; + } + else if (is_avro_int64(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.int_value = avro_resolver_int_long_value; + } + else if (is_avro_double(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.int_value = avro_resolver_int_double_value; + } + else if (is_avro_float(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.int_value = avro_resolver_int_float_value; + } + return 0; +} + + +static int +avro_resolver_long_value(avro_consumer_t *consumer, int64_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId64 " into %p", value, dest); + return avro_int64_set(dest, value); +} + +static int +avro_resolver_long_float_value(avro_consumer_t *consumer, int64_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId64 " into %p (promoting long to float)", value, dest); + return avro_float_set(dest, (const float) value); +} + +static int +avro_resolver_long_double_value(avro_consumer_t *consumer, int64_t value, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing %" PRId64 " into %p (promoting long to double)", value, dest); + return avro_double_set(dest, (const double) value); +} + +static int +try_long(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_int64(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.long_value = avro_resolver_long_value; + } + else if (is_avro_double(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.long_value = avro_resolver_long_double_value; + } + else if (is_avro_float(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.long_value = avro_resolver_long_float_value; + } + return 0; +} + + +static int +avro_resolver_null_value(avro_consumer_t *consumer, void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + + AVRO_UNUSED(dest); + debug("Storing null into %p", dest); + return 0; +} + +static int +try_null(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_null(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.null_value = avro_resolver_null_value; + } + return 0; +} + + +static int +avro_resolver_string_value(avro_consumer_t *consumer, + const void *value, size_t value_len, + void *user_data) +{ + AVRO_UNUSED(value_len); + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing \"%s\" into %p", (const char *) value, dest); + return avro_givestring_set(dest, (const char *) value, avro_alloc_free_func); +} + +static int +try_string(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + if (is_avro_string(rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.string_value = avro_resolver_string_value; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * arrays + */ + +static int +avro_resolver_array_start_block(avro_consumer_t *consumer, + int is_first_block, + unsigned int block_count, + void *user_data) +{ + if (is_first_block) { + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + AVRO_UNUSED(dest); + + debug("Starting array %p", dest); + } + + AVRO_UNUSED(block_count); + return 0; +} + +static int +avro_resolver_array_element(avro_consumer_t *consumer, + unsigned int index, + avro_consumer_t **element_consumer, + void **element_user_data, + void *user_data) +{ + AVRO_UNUSED(index); + + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Adding element to array %p", dest); + + /* + * Allocate a new element datum and add it to the array. + */ + + avro_schema_t array_schema = avro_datum_get_schema(dest); + avro_schema_t item_schema = avro_schema_array_items(array_schema); + avro_datum_t element = avro_datum_from_schema(item_schema); + avro_array_append_datum(dest, element); + avro_datum_decref(element); + + /* + * Return the consumer that we allocated to process the array's + * children. + */ + + *element_consumer = resolver->child_resolvers[0]; + *element_user_data = element; + return 0; +} + +static int +try_array(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * First verify that the reader is an array. + */ + + if (!is_avro_array(rschema)) { + return 0; + } + + /* + * Array schemas have to have compatible element schemas to be + * compatible themselves. Try to create an avro_resolver_t to + * check the compatibility. + */ + + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + + avro_schema_t witems = avro_schema_array_items(wschema); + avro_schema_t ritems = avro_schema_array_items(rschema); + + avro_consumer_t *item_consumer = + avro_resolver_new_memoized(mem, witems, ritems); + if (!item_consumer) { + avro_memoize_delete(mem, wschema, root_rschema); + avro_consumer_free(&(*resolver)->parent); + avro_prefix_error("Array values aren't compatible: "); + return EINVAL; + } + + /* + * The two schemas are compatible, so go ahead and create a + * GavroResolver for the array. Store the item schema's + * resolver into the child_resolvers field. + */ + + (*resolver)->num_children = 1; + (*resolver)->child_resolvers = (avro_consumer_t **) avro_calloc(1, sizeof(avro_consumer_t *)); + (*resolver)->child_resolvers[0] = item_consumer; + (*resolver)->parent.array_start_block = avro_resolver_array_start_block; + (*resolver)->parent.array_element = avro_resolver_array_element; + + return 0; +} + + +/*----------------------------------------------------------------------- + * enums + */ + +static int +avro_resolver_enum_value(avro_consumer_t *consumer, int value, + void *user_data) +{ + AVRO_UNUSED(value); + + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + + const char *symbol_name = avro_schema_enum_get(resolver->parent.schema, value); + debug("Storing symbol %s into %p", symbol_name, dest); + return avro_enum_set_name(dest, symbol_name); +} + +static int +try_enum(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * Enum schemas have to have the same name — but not the same + * list of symbols — to be compatible. + */ + + if (is_avro_enum(rschema)) { + const char *wname = avro_schema_name(wschema); + const char *rname = avro_schema_name(rschema); + + if (!strcmp(wname, rname)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.enum_value = avro_resolver_enum_value; + } + } + return 0; +} + + +/*----------------------------------------------------------------------- + * fixed + */ + +static int +avro_resolver_fixed_value(avro_consumer_t *consumer, + const void *value, size_t value_len, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Storing (fixed) %" PRIsz " bytes into %p", value_len, dest); + return avro_givefixed_set(dest, (const char *) value, value_len, avro_alloc_free_func); +} + +static int +try_fixed(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * Fixed schemas need the same name and size to be compatible. + */ + + if (avro_schema_equal(wschema, rschema)) { + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + (*resolver)->parent.fixed_value = avro_resolver_fixed_value; + } + return 0; +} + + +/*----------------------------------------------------------------------- + * maps + */ + +static int +avro_resolver_map_start_block(avro_consumer_t *consumer, + int is_first_block, + unsigned int block_count, + void *user_data) +{ + if (is_first_block) { + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + AVRO_UNUSED(dest); + + debug("Starting map %p", dest); + } + + AVRO_UNUSED(block_count); + return 0; +} + +static int +avro_resolver_map_element(avro_consumer_t *consumer, + unsigned int index, + const char *key, + avro_consumer_t **value_consumer, + void **value_user_data, + void *user_data) +{ + AVRO_UNUSED(index); + + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + debug("Adding element to map %p", dest); + + /* + * Allocate a new element datum and add it to the map. + */ + + avro_schema_t map_schema = avro_datum_get_schema(dest); + avro_schema_t value_schema = avro_schema_map_values(map_schema); + avro_datum_t value = avro_datum_from_schema(value_schema); + avro_map_set(dest, key, value); + avro_datum_decref(value); + + /* + * Return the consumer that we allocated to process the map's + * children. + */ + + *value_consumer = resolver->child_resolvers[0]; + *value_user_data = value; + return 0; +} + +static int +try_map(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * First verify that the reader is an map. + */ + + if (!is_avro_map(rschema)) { + return 0; + } + + /* + * Array schemas have to have compatible element schemas to be + * compatible themselves. Try to create an avro_resolver_t to + * check the compatibility. + */ + + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + + avro_schema_t wvalues = avro_schema_map_values(wschema); + avro_schema_t rvalues = avro_schema_map_values(rschema); + + avro_consumer_t *value_consumer = + avro_resolver_new_memoized(mem, wvalues, rvalues); + if (!value_consumer) { + avro_memoize_delete(mem, wschema, root_rschema); + avro_consumer_free(&(*resolver)->parent); + avro_prefix_error("Map values aren't compatible: "); + return EINVAL; + } + + /* + * The two schemas are compatible, so go ahead and create a + * GavroResolver for the map. Store the value schema's + * resolver into the child_resolvers field. + */ + + (*resolver)->num_children = 1; + (*resolver)->child_resolvers = (avro_consumer_t **) avro_calloc(1, sizeof(avro_consumer_t *)); + (*resolver)->child_resolvers[0] = value_consumer; + (*resolver)->parent.map_start_block = avro_resolver_map_start_block; + (*resolver)->parent.map_element = avro_resolver_map_element; + + return 0; +} + + +/*----------------------------------------------------------------------- + * records + */ + +static int +avro_resolver_record_start(avro_consumer_t *consumer, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + AVRO_UNUSED(dest); + + debug("Starting record at %p", dest); + + /* + * TODO: Eventually, we'll fill in default values for the extra + * reader fields here. + */ + + return 0; +} + +static int +avro_resolver_record_field(avro_consumer_t *consumer, + unsigned int index, + avro_consumer_t **field_consumer, + void **field_user_data, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + avro_datum_t ud_dest = (avro_datum_t) user_data; + avro_datum_t dest = avro_resolver_get_real_dest(resolver, ud_dest); + + const char *field_name = + avro_schema_record_field_name(consumer->schema, index); + + /* + * Grab the resolver for this field of the writer record. If + * it's NULL, this this field doesn't exist in the reader + * record, and should be skipped. + */ + + debug("Retrieving resolver for writer field %i (%s)", + index, field_name); + + if (!resolver->child_resolvers[index]) { + debug("Reader doesn't have field %s, skipping", field_name); + return 0; + } + + /* + * TODO: Once we can retrieve record fields by index (quickly), + * use the index_mapping. + */ + + avro_datum_t field = NULL; + avro_record_get(dest, field_name, &field); + + *field_consumer = resolver->child_resolvers[index]; + *field_user_data = field; + return 0; +} + +static int +try_record(avro_memoize_t *mem, avro_resolver_t **resolver, + avro_schema_t wschema, avro_schema_t rschema, + avro_schema_t root_rschema) +{ + /* + * First verify that the reader is also a record, and has the + * same name as the writer. + */ + + if (!is_avro_record(rschema)) { + return 0; + } + + const char *wname = avro_schema_name(wschema); + const char *rname = avro_schema_name(rschema); + + if (strcmp(wname, rname)) { + return 0; + } + + /* + * Categorize the fields in the record schemas. Fields that are + * only in the writer are ignored. Fields that are only in the + * reader raise a schema mismatch error, unless the field has a + * default value. Fields that are in both are resolved + * recursively. + * + * The child_resolver array will contain an avro_resolver_t for + * each field in the writer schema. To build this array, we + * loop through the fields of the reader schema. If that field + * is also in the writer schema, we resolve them recursively, + * and store the resolver into the array. If the field isn't in + * the writer schema, we raise an error. (TODO: Eventually, + * we'll handle default values here.) After this loop finishes, + * any NULLs in the child_resolver array will represent fields + * in the writer but not the reader; these fields will be + * skipped when processing the input. + */ + + *resolver = avro_resolver_create(wschema, root_rschema); + avro_memoize_set(mem, wschema, root_rschema, *resolver); + + size_t wfields = avro_schema_record_size(wschema); + size_t rfields = avro_schema_record_size(rschema); + + debug("Checking writer record schema %s", wname); + + avro_consumer_t **child_resolvers = + (avro_consumer_t **) avro_calloc(wfields, sizeof(avro_consumer_t *)); + int *index_mapping = (int *) avro_calloc(wfields, sizeof(int)); + + unsigned int ri; + for (ri = 0; ri < rfields; ri++) { + avro_schema_t rfield = + avro_schema_record_field_get_by_index(rschema, ri); + const char *field_name = + avro_schema_record_field_name(rschema, ri); + + debug("Resolving reader record field %u (%s)", ri, field_name); + + /* + * See if this field is also in the writer schema. + */ + + int wi = avro_schema_record_field_get_index(wschema, field_name); + + if (wi == -1) { + /* + * This field isn't in the writer schema — + * that's an error! TODO: Handle default + * values! + */ + + debug("Field %s isn't in writer", field_name); + avro_set_error("Reader field %s doesn't appear in writer", + field_name); + goto error; + } + + /* + * Try to recursively resolve the schemas for this + * field. If they're not compatible, that's an error. + */ + + avro_schema_t wfield = + avro_schema_record_field_get_by_index(wschema, wi); + avro_consumer_t *field_resolver = + avro_resolver_new_memoized(mem, wfield, rfield); + + if (!field_resolver) { + avro_prefix_error("Field %s isn't compatible: ", field_name); + goto error; + } + + /* + * Save the details for this field. + */ + + debug("Found match for field %s (%u in reader, %d in writer)", + field_name, ri, wi); + child_resolvers[wi] = field_resolver; + index_mapping[wi] = ri; + } + + /* + * We might not have found matches for all of the writer fields, + * but that's okay — any extras will be ignored. + */ + + (*resolver)->num_children = wfields; + (*resolver)->child_resolvers = child_resolvers; + (*resolver)->index_mapping = index_mapping; + (*resolver)->parent.record_start = avro_resolver_record_start; + (*resolver)->parent.record_field = avro_resolver_record_field; + return 0; + +error: + /* + * Clean up any consumer we might have already created. + */ + + avro_memoize_delete(mem, wschema, root_rschema); + avro_consumer_free(&(*resolver)->parent); + + { + unsigned int i; + for (i = 0; i < wfields; i++) { + if (child_resolvers[i]) { + avro_consumer_free(child_resolvers[i]); + } + } + } + + avro_free(child_resolvers, wfields * sizeof(avro_consumer_t *)); + avro_free(index_mapping, wfields * sizeof(int)); + return EINVAL; +} + + +/*----------------------------------------------------------------------- + * union + */ + +static int +avro_resolver_union_branch(avro_consumer_t *consumer, + unsigned int discriminant, + avro_consumer_t **branch_consumer, + void **branch_user_data, + void *user_data) +{ + avro_resolver_t *resolver = (avro_resolver_t *) consumer; + + /* + * Grab the resolver for this branch of the writer union. If + * it's NULL, then this branch is incompatible with the reader. + */ + + debug("Retrieving resolver for writer branch %u", discriminant); + + if (!resolver->child_resolvers[discriminant]) { + avro_set_error("Writer union branch %u is incompatible " + "with reader schema \"%s\"", + discriminant, avro_schema_type_name(resolver->rschema)); + return EINVAL; + } + + /* + * Return the branch's resolver. + */ + + *branch_consumer = resolver->child_resolvers[discriminant]; + *branch_user_data = user_data; + return 0; +} + +static avro_consumer_t * +try_union(avro_memoize_t *mem, avro_schema_t wschema, avro_schema_t rschema) +{ + /* + * For a writer union, we recursively try to resolve each branch + * against the reader schema. This will work correctly whether + * or not the reader is also a union — if the reader is a union, + * then we'll resolve each (non-union) writer branch against the + * reader union, which will be checked in our calls to + * check_simple_writer below. The net result is that we might + * end up trying every combination of writer and reader + * branches, when looking for compatible schemas. + * + * Regardless of what the reader schema is, for each writer + * branch, we stash away the recursive avro_resolver_t into the + * child_resolvers array. A NULL entry in this array means that + * that branch isn't compatible with the reader. This isn't an + * immediate schema resolution error, since we allow + * incompatible branches in the types as long as that branch + * never appears in the actual data. We only return an error if + * there are *no* branches that are compatible. + */ + + size_t num_branches = avro_schema_union_size(wschema); + debug("Checking %" PRIsz "-branch writer union schema", num_branches); + + avro_resolver_t *resolver = avro_resolver_create(wschema, rschema); + avro_memoize_set(mem, wschema, rschema, resolver); + + avro_consumer_t **child_resolvers = + (avro_consumer_t **) avro_calloc(num_branches, sizeof(avro_consumer_t *)); + int some_branch_compatible = 0; + + unsigned int i; + for (i = 0; i < num_branches; i++) { + avro_schema_t branch_schema = + avro_schema_union_branch(wschema, i); + + debug("Resolving writer union branch %u (%s)", + i, avro_schema_type_name(branch_schema)); + + /* + * Try to recursively resolve this branch of the writer + * union. Don't raise an error if this fails — it's + * okay for some of the branches to not be compatible + * with the reader, as long as those branches never + * appear in the input. + */ + + child_resolvers[i] = + avro_resolver_new_memoized(mem, branch_schema, rschema); + if (child_resolvers[i]) { + debug("Found match for writer union branch %u", i); + some_branch_compatible = 1; + } else { + debug("No match for writer union branch %u", i); + } + } + + /* + * As long as there's at least one branch that's compatible with + * the reader, then we consider this schema resolution a + * success. + */ + + if (!some_branch_compatible) { + debug("No writer union branches match"); + avro_set_error("No branches in the writer are compatible " + "with reader schema %s", + avro_schema_type_name(rschema)); + goto error; + } + + resolver->num_children = num_branches; + resolver->child_resolvers = child_resolvers; + resolver->parent.union_branch = avro_resolver_union_branch; + return &resolver->parent; + +error: + /* + * Clean up any consumer we might have already created. + */ + + avro_memoize_delete(mem, wschema, rschema); + avro_consumer_free(&resolver->parent); + + for (i = 0; i < num_branches; i++) { + if (child_resolvers[i]) { + avro_consumer_free(child_resolvers[i]); + } + } + + avro_free(child_resolvers, num_branches * sizeof(avro_consumer_t *)); + return NULL; +} + + +/*----------------------------------------------------------------------- + * schema type dispatcher + */ + +static avro_consumer_t * +avro_resolver_new_memoized(avro_memoize_t *mem, + avro_schema_t wschema, avro_schema_t rschema) +{ + check_param(NULL, is_avro_schema(wschema), "writer schema"); + check_param(NULL, is_avro_schema(rschema), "reader schema"); + + skip_links(wschema); + skip_links(rschema); + + /* + * First see if we've already matched these two schemas. If so, + * just return that resolver. + */ + + avro_resolver_t *saved = NULL; + if (avro_memoize_get(mem, wschema, rschema, (void **) &saved)) { + debug("Already resolved %s and %s", + avro_schema_type_name(wschema), + avro_schema_type_name(rschema)); + return &saved->parent; + } + + /* + * Otherwise we have some work to do. + */ + + switch (avro_typeof(wschema)) + { + case AVRO_BOOLEAN: + check_simple_writer(mem, wschema, rschema, boolean); + return NULL; + + case AVRO_BYTES: + check_simple_writer(mem, wschema, rschema, bytes); + return NULL; + + case AVRO_DOUBLE: + check_simple_writer(mem, wschema, rschema, double); + return NULL; + + case AVRO_FLOAT: + check_simple_writer(mem, wschema, rschema, float); + return NULL; + + case AVRO_INT32: + check_simple_writer(mem, wschema, rschema, int); + return NULL; + + case AVRO_INT64: + check_simple_writer(mem, wschema, rschema, long); + return NULL; + + case AVRO_NULL: + check_simple_writer(mem, wschema, rschema, null); + return NULL; + + case AVRO_STRING: + check_simple_writer(mem, wschema, rschema, string); + return NULL; + + case AVRO_ARRAY: + check_simple_writer(mem, wschema, rschema, array); + return NULL; + + case AVRO_ENUM: + check_simple_writer(mem, wschema, rschema, enum); + return NULL; + + case AVRO_FIXED: + check_simple_writer(mem, wschema, rschema, fixed); + return NULL; + + case AVRO_MAP: + check_simple_writer(mem, wschema, rschema, map); + return NULL; + + case AVRO_RECORD: + check_simple_writer(mem, wschema, rschema, record); + return NULL; + + case AVRO_UNION: + return try_union(mem, wschema, rschema); + + default: + avro_set_error("Unknown schema type"); + return NULL; + } + + return NULL; +} + + +avro_consumer_t * +avro_resolver_new(avro_schema_t wschema, avro_schema_t rschema) +{ + avro_memoize_t mem; + avro_memoize_init(&mem); + avro_consumer_t *result = + avro_resolver_new_memoized(&mem, wschema, rschema); + avro_memoize_done(&mem); + return result; +} diff --git a/fluent-bit/lib/avro/src/schema.c b/fluent-bit/lib/avro/src/schema.c new file mode 100644 index 000000000..7b389002b --- /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); +} diff --git a/fluent-bit/lib/avro/src/schema.h b/fluent-bit/lib/avro/src/schema.h new file mode 100644 index 000000000..3c99ee630 --- /dev/null +++ b/fluent-bit/lib/avro/src/schema.h @@ -0,0 +1,87 @@ +/* + * 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. + */ +#ifndef AVRO_SCHEMA_PRIV_H +#define AVRO_SCHEMA_PRIV_H + +#include <avro/platform.h> +#include "avro/basics.h" +#include "avro/schema.h" +#include "avro_private.h" +#include "st.h" + +struct avro_record_field_t { + int index; + char *name; + avro_schema_t type; + /* + * TODO: default values + */ +}; + +struct avro_record_schema_t { + struct avro_obj_t obj; + char *name; + char *space; + st_table *fields; + st_table *fields_byname; +}; + +struct avro_enum_schema_t { + struct avro_obj_t obj; + char *name; + char *space; + st_table *symbols; + st_table *symbols_byname; +}; + +struct avro_array_schema_t { + struct avro_obj_t obj; + avro_schema_t items; +}; + +struct avro_map_schema_t { + struct avro_obj_t obj; + avro_schema_t values; +}; + +struct avro_union_schema_t { + struct avro_obj_t obj; + st_table *branches; + st_table *branches_byname; +}; + +struct avro_fixed_schema_t { + struct avro_obj_t obj; + const char *name; + const char *space; + int64_t size; +}; + +struct avro_link_schema_t { + struct avro_obj_t obj; + avro_schema_t to; +}; + +#define avro_schema_to_record(schema_) (container_of(schema_, struct avro_record_schema_t, obj)) +#define avro_schema_to_enum(schema_) (container_of(schema_, struct avro_enum_schema_t, obj)) +#define avro_schema_to_array(schema_) (container_of(schema_, struct avro_array_schema_t, obj)) +#define avro_schema_to_map(schema_) (container_of(schema_, struct avro_map_schema_t, obj)) +#define avro_schema_to_union(schema_) (container_of(schema_, struct avro_union_schema_t, obj)) +#define avro_schema_to_fixed(schema_) (container_of(schema_, struct avro_fixed_schema_t, obj)) +#define avro_schema_to_link(schema_) (container_of(schema_, struct avro_link_schema_t, obj)) + +#endif diff --git a/fluent-bit/lib/avro/src/schema_equal.c b/fluent-bit/lib/avro/src/schema_equal.c new file mode 100644 index 000000000..419ed1278 --- /dev/null +++ b/fluent-bit/lib/avro/src/schema_equal.c @@ -0,0 +1,204 @@ +/* + * 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_private.h" +#include "schema.h" +#include <string.h> + +static int +schema_record_equal(struct avro_record_schema_t *a, + struct avro_record_schema_t *b) +{ + long i; + if (strcmp(a->name, b->name)) { + /* + * They have different names + */ + return 0; + } + if (nullstrcmp(a->space, b->space)) { + return 0; + } + if (a->fields->num_entries != b->fields->num_entries) { + /* They have different numbers of fields */ + return 0; + } + for (i = 0; i < a->fields->num_entries; i++) { + union { + st_data_t data; + struct avro_record_field_t *f; + } fa, fb; + st_lookup(a->fields, i, &fa.data); + if (!st_lookup(b->fields, i, &fb.data)) { + return 0; + } + if (strcmp(fa.f->name, fb.f->name)) { + /* + * They have fields with different names + */ + return 0; + } + if (!avro_schema_equal(fa.f->type, fb.f->type)) { + /* + * They have fields with different schemas + */ + return 0; + } + } + return 1; +} + +static int +schema_enum_equal(struct avro_enum_schema_t *a, struct avro_enum_schema_t *b) +{ + long i; + if (strcmp(a->name, b->name)) { + /* + * They have different names + */ + return 0; + } + if (nullstrcmp(a->space, b->space)) { + return 0; + } + for (i = 0; i < a->symbols->num_entries; i++) { + union { + st_data_t data; + char *sym; + } sa, sb; + st_lookup(a->symbols, i, &sa.data); + if (!st_lookup(b->symbols, i, &sb.data)) { + return 0; + } + if (strcmp(sa.sym, sb.sym) != 0) { + /* + * They have different symbol names + */ + return 0; + } + } + return 1; +} + +static int +schema_fixed_equal(struct avro_fixed_schema_t *a, struct avro_fixed_schema_t *b) +{ + if (strcmp(a->name, b->name)) { + /* + * They have different names + */ + return 0; + } + if (nullstrcmp(a->space, b->space)) { + return 0; + } + return (a->size == b->size); +} + +static int +schema_map_equal(struct avro_map_schema_t *a, struct avro_map_schema_t *b) +{ + return avro_schema_equal(a->values, b->values); +} + +static int +schema_array_equal(struct avro_array_schema_t *a, struct avro_array_schema_t *b) +{ + return avro_schema_equal(a->items, b->items); +} + +static int +schema_union_equal(struct avro_union_schema_t *a, struct avro_union_schema_t *b) +{ + long i; + for (i = 0; i < a->branches->num_entries; i++) { + union { + st_data_t data; + avro_schema_t schema; + } ab, bb; + st_lookup(a->branches, i, &ab.data); + if (!st_lookup(b->branches, i, &bb.data)) { + return 0; + } + if (!avro_schema_equal(ab.schema, bb.schema)) { + /* + * They don't have the same schema types + */ + return 0; + } + } + return 1; +} + +static int +schema_link_equal(struct avro_link_schema_t *a, struct avro_link_schema_t *b) +{ + /* + * NOTE: links can only be used for named types. They are used in + * recursive schemas so we just check the name of the schema pointed + * to instead of a deep check. Otherwise, we recurse forever... + */ + if (is_avro_record(a->to)) { + if (!is_avro_record(b->to)) { + return 0; + } + if (nullstrcmp(avro_schema_to_record(a->to)->space, + avro_schema_to_record(b->to)->space)) { + return 0; + } + } + return (strcmp(avro_schema_name(a->to), avro_schema_name(b->to)) == 0); +} + +int avro_schema_equal(avro_schema_t a, avro_schema_t b) +{ + if (!a || !b) { + /* + * this is an error. protecting from segfault. + */ + return 0; + } else if (a == b) { + /* + * an object is equal to itself + */ + return 1; + } else if (avro_typeof(a) != avro_typeof(b)) { + return 0; + } else if (is_avro_record(a)) { + return schema_record_equal(avro_schema_to_record(a), + avro_schema_to_record(b)); + } else if (is_avro_enum(a)) { + return schema_enum_equal(avro_schema_to_enum(a), + avro_schema_to_enum(b)); + } else if (is_avro_fixed(a)) { + return schema_fixed_equal(avro_schema_to_fixed(a), + avro_schema_to_fixed(b)); + } else if (is_avro_map(a)) { + return schema_map_equal(avro_schema_to_map(a), + avro_schema_to_map(b)); + } else if (is_avro_array(a)) { + return schema_array_equal(avro_schema_to_array(a), + avro_schema_to_array(b)); + } else if (is_avro_union(a)) { + return schema_union_equal(avro_schema_to_union(a), + avro_schema_to_union(b)); + } else if (is_avro_link(a)) { + return schema_link_equal(avro_schema_to_link(a), + avro_schema_to_link(b)); + } + return 1; +} diff --git a/fluent-bit/lib/avro/src/schema_specific.c b/fluent-bit/lib/avro/src/schema_specific.c new file mode 100644 index 000000000..7a0150c5f --- /dev/null +++ b/fluent-bit/lib/avro/src/schema_specific.c @@ -0,0 +1,232 @@ +/* + * 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_private.h" +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include "schema.h" + +enum specific_state { + START_STATE, +}; +typedef enum specific_state specific_state; + +struct specific_ctx { + FILE *header; + FILE *source; + int depth; + specific_state state; +}; +typedef struct specific_ctx specific_ctx; + +static void indent(specific_ctx * ctx, FILE * fp) +{ + int i; + for (i = 0; i < ctx->depth; i++) { + fprintf(fp, " "); + } +} + +static int avro_schema_to_source(avro_schema_t schema, specific_ctx * ctx) +{ + switch (schema->type) { + default: + return 0; + } + return EINVAL; +} + +static int avro_schema_to_header(avro_schema_t schema, specific_ctx * ctx) +{ + size_t i; + FILE *fp = ctx->header; + + indent(ctx, fp); + ctx->depth++; + + if (is_avro_primitive(schema) && !ctx->name) { + return 0; + } + + switch (schema->type) { + case AVRO_STRING: + fprintf(fp, "char *%s;\n", ctx->name); + break; + + case AVRO_BYTES: + fprintf(fp, "struct %s { size_t %s_len; char *%s_val } %s;\n", + ctx->name, ctx->name, ctx->name, ctx->name); + break; + + case AVRO_INT: + fprintf(fp, "int %s;\n", ctx->name); + break; + + case AVRO_LONG: + fprintf(fp, "long %s;\n", ctx->name); + break; + + case AVRO_FLOAT: + fprintf(fp, "float %s;\n", ctx->name); + break; + + case AVRO_DOUBLE: + fprintf(fp, "double %s;\n", ctx->name); + break; + + case AVRO_BOOLEAN: + fprintf(fp, "int %s; /* boolean */\n", ctx->name); + break; + + case AVRO_NULL: + break; + + case AVRO_RECORD: + { + struct schema_record_t *record_schema = + avro_schema_to_record(schema); + fprintf(fp, "struct %s {\n", record_schema->name); + for (i = 0; i < record_schema->num_fields; i++) { + struct record_field_t *field = + record_schema->fields[i]; + ctx->name = field->name; + avro_schema_to_header(field->type, ctx); + ctx->name = NULL; + } + fprintf(fp, "};\n"); + fprintf(fp, "typedef struct %s %s;\n\n", + record_schema->name, record_schema->name); + } + break; + + case AVRO_ENUM: + { + struct schema_enum_t *enum_schema = + avro_schema_to_enum(schema); + fprintf(fp, "enum %s {\n", enum_schema->name); + ctx->depth++; + for (i = 0; i < enum_schema->num_symbols; i++) { + indent(ctx, fp); + fprintf(fp, "%s = %ld,\n", + enum_schema->symbols[i], i); + } + ctx->depth--; + fprintf(fp, "};\n"); + fprintf(fp, "typedef enum %s %s;\n\n", + enum_schema->name, enum_schema->name); + } + break; + + case AVRO_FIXED: + { + struct schema_fixed_t *fixed_schema = + avro_schema_to_fixed(schema); + fprintf(fp, "char %s[%ld];\n", fixed_schema->name, + fixed_schema->size); + } + break; + + case AVRO_MAP: + { + + } + break; + + case AVRO_ARRAY: + { + struct schema_array_t *array_schema = + avro_schema_to_array(schema); + if (!ctx->name) { + break; + } + fprintf(fp, "struct { size_t %s_len; ", ctx->name); + if (is_avro_named_type(array_schema->items)) { + fprintf(fp, "%s", + avro_schema_name(array_schema->items)); + } else if (is_avro_link(array_schema->items)) { + struct schema_link_t *link_schema = + avro_schema_to_link(array_schema->items); + fprintf(fp, "struct %s", + avro_schema_name(link_schema->to)); + } else { + avro_schema_to_header(array_schema->items, ctx); + } + fprintf(fp, " *%s_val;} %s;\n", ctx->name, ctx->name); + } + break; + case AVRO_UNION: + { + struct schema_union_t *union_schema = + avro_schema_to_array(schema); + if (!ctx->name) { + break; + } + fprintf(fp, "union {\n"); + for (i = 0; i < union_schema->num_schemas; i++) { + avro_schema_to_header(union_schema->schemas[i], + ctx); + } + fprintf(fp, "%s_u;\n"); + } + break; + case AVRO_LINK: + break; + default: + return EINVAL; + } + + ctx->depth--; + return 0; +} + +int avro_schema_to_specific(avro_schema_t schema, const char *prefix) +{ + specific_ctx ctx; + char buf[1024]; + int rval; + + if (!schema) { + return EINVAL; + } + + memset(&ctx, 0, sizeof(ctx)); + snprintf(buf, sizeof(buf), "%s_avro.h", prefix); + ctx.header = fopen(buf, "w"); + if (!ctx.header) { + return errno; + } + snprintf(buf, sizeof(buf), "%s_avro.c", prefix); + ctx.source = fopen(buf, "w"); + if (!ctx.source) { + fclose(ctx.header); + return errno; + } + + rval = avro_schema_to_header(schema, &ctx); + if (rval) { + goto out; + } + + rval = avro_schema_to_source(schema, &ctx); + + out: + fclose(ctx.header); + fclose(ctx.source); + return rval; +} diff --git a/fluent-bit/lib/avro/src/st.c b/fluent-bit/lib/avro/src/st.c new file mode 100644 index 000000000..27578289e --- /dev/null +++ b/fluent-bit/lib/avro/src/st.c @@ -0,0 +1,543 @@ +/* + * This is a public domain general purpose hash table package written by + * Peter Moore @ UCB. + */ + +/* + * static char sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; + */ + +#include "avro_private.h" +#include "avro/allocation.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "st.h" + +typedef struct st_table_entry st_table_entry; + +struct st_table_entry { + unsigned int hash; + st_data_t key; + st_data_t record; + st_table_entry *next; +}; + +#define ST_DEFAULT_MAX_DENSITY 5 +#define ST_DEFAULT_INIT_TABLE_SIZE 11 + + /* + * DEFAULT_MAX_DENSITY is the default for the largest we allow the + * average number of items per bin before increasing the number of + * bins + * + * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins + * allocated initially + * + */ +static int numcmp(long, long); +static int numhash(long); +static struct st_hash_type type_numhash = { + HASH_FUNCTION_CAST numcmp, + HASH_FUNCTION_CAST numhash +}; + +/* + * extern int strcmp(const char *, const char *); + */ +static int strhash(const char *); +static struct st_hash_type type_strhash = { + HASH_FUNCTION_CAST strcmp, + HASH_FUNCTION_CAST strhash +}; + +static void rehash(st_table *); + +#ifdef RUBY +#define malloc xmalloc +#define calloc xcalloc +#endif + +#define Calloc(n,s) (char*)avro_calloc((n),(s)) + +#define free_bins(tbl) \ + avro_free(tbl->bins, tbl->num_bins * sizeof(st_table_entry *)) + +#define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y)) == 0) + +#define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key)) +#define do_hash_bin(key,table) (do_hash(key, table)%(table)->num_bins) + +/* + * MINSIZE is the minimum size of a dictionary. + */ + +#define MINSIZE 8 + +/* + * Table of prime numbers 2^n+a, 2<=n<=30. + */ +static long primes[] = { + 8 + 3, + 16 + 3, + 32 + 5, + 64 + 3, + 128 + 3, + 256 + 27, + 512 + 9, + 1024 + 9, + 2048 + 5, + 4096 + 3, + 8192 + 27, + 16384 + 43, + 32768 + 3, + 65536 + 45, + 131072 + 29, + 262144 + 3, + 524288 + 21, + 1048576 + 7, + 2097152 + 17, + 4194304 + 15, + 8388608 + 9, + 16777216 + 43, + 33554432 + 35, + 67108864 + 15, + 134217728 + 29, + 268435456 + 3, + 536870912 + 11, + 1073741824 + 85, + 0 +}; + +static int new_size(int size) +{ + unsigned int i; + +#if 0 + for (i = 3; i < 31; i++) { + if ((1 << i) > size) + return 1 << i; + } + return -1; +#else + int newsize; + + for (i = 0, newsize = MINSIZE; + i < sizeof(primes) / sizeof(primes[0]); i++, newsize <<= 1) { + if (newsize > size) + return primes[i]; + } + /* + * Ran out of polynomials + */ + return -1; /* should raise exception */ +#endif +} + +#ifdef HASH_LOG +static int collision = 0; +static int init_st = 0; + +static void stat_col() +{ + FILE *f = fopen("/tmp/col", "w"); + fprintf(f, "collision: %d\n", collision); + fclose(f); +} +#endif + +st_table *st_init_table_with_size(struct st_hash_type *type, int size) +{ + st_table *tbl; + +#ifdef HASH_LOG + if (init_st == 0) { + init_st = 1; + atexit(stat_col); + } +#endif + + size = new_size(size); /* round up to prime number */ + + tbl = (st_table *) avro_new(st_table); + tbl->type = type; + tbl->num_entries = 0; + tbl->num_bins = size; + tbl->bins = (st_table_entry **) Calloc(size, sizeof(st_table_entry *)); + + return tbl; +} + +st_table *st_init_table(struct st_hash_type *type) +{ + return st_init_table_with_size(type, 0); +} + +st_table *st_init_numtable(void) +{ + return st_init_table(&type_numhash); +} + +st_table *st_init_numtable_with_size(int size) +{ + return st_init_table_with_size(&type_numhash, size); +} + +st_table *st_init_strtable(void) +{ + return st_init_table(&type_strhash); +} + +st_table *st_init_strtable_with_size(int size) +{ + return st_init_table_with_size(&type_strhash, size); +} + +void st_free_table(st_table *table) +{ + register st_table_entry *ptr, *next; + int i; + + for (i = 0; i < table->num_bins; i++) { + ptr = table->bins[i]; + while (ptr != 0) { + next = ptr->next; + avro_freet(st_table_entry, ptr); + ptr = next; + } + } + free_bins(table); + avro_freet(st_table, table); +} + +#define PTR_NOT_EQUAL(table, ptr, hash_val, key) \ +((ptr) != 0 && (ptr->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key))) + +#ifdef HASH_LOG +#define COLLISION collision++ +#else +#define COLLISION +#endif + +#define FIND_ENTRY(table, ptr, hash_val, bin_pos) do {\ + bin_pos = hash_val%(table)->num_bins;\ + ptr = (table)->bins[bin_pos];\ + if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {\ + COLLISION;\ + while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {\ + ptr = ptr->next;\ + }\ + ptr = ptr->next;\ + }\ +} while (0) + +int st_lookup(st_table *table, register st_data_t key, st_data_t *value) +{ + unsigned int hash_val, bin_pos; + register st_table_entry *ptr; + + hash_val = do_hash(key, table); + FIND_ENTRY(table, ptr, hash_val, bin_pos); + + if (ptr == 0) { + return 0; + } else { + if (value != 0) + *value = ptr->record; + return 1; + } +} + +#define ADD_DIRECT(table, key, value, hash_val, bin_pos)\ +do {\ + st_table_entry *entry;\ + if (table->num_entries/(table->num_bins) > ST_DEFAULT_MAX_DENSITY) {\ + rehash(table);\ + bin_pos = hash_val % table->num_bins;\ + }\ + \ + entry = (st_table_entry *) avro_new(st_table_entry);\ + \ + entry->hash = hash_val;\ + entry->key = key;\ + entry->record = value;\ + entry->next = table->bins[bin_pos];\ + table->bins[bin_pos] = entry;\ + table->num_entries++;\ +} while (0) + +int st_insert(register st_table *table, register st_data_t key, st_data_t value) +{ + unsigned int hash_val, bin_pos; + register st_table_entry *ptr; + + hash_val = do_hash(key, table); + FIND_ENTRY(table, ptr, hash_val, bin_pos); + + if (ptr == 0) { + ADD_DIRECT(table, key, value, hash_val, bin_pos); + return 0; + } else { + ptr->record = value; + return 1; + } +} + +void st_add_direct(st_table *table,st_data_t key,st_data_t value) +{ + unsigned int hash_val, bin_pos; + + hash_val = do_hash(key, table); + bin_pos = hash_val % table->num_bins; + ADD_DIRECT(table, key, value, hash_val, bin_pos); +} + +static void rehash(register st_table *table) +{ + register st_table_entry *ptr, *next, **new_bins; + int i, old_num_bins = table->num_bins, new_num_bins; + unsigned int hash_val; + + new_num_bins = new_size(old_num_bins + 1); + new_bins = + (st_table_entry **) Calloc(new_num_bins, sizeof(st_table_entry *)); + + for (i = 0; i < old_num_bins; i++) { + ptr = table->bins[i]; + while (ptr != 0) { + next = ptr->next; + hash_val = ptr->hash % new_num_bins; + ptr->next = new_bins[hash_val]; + new_bins[hash_val] = ptr; + ptr = next; + } + } + free_bins(table); + table->num_bins = new_num_bins; + table->bins = new_bins; +} + +st_table *st_copy(st_table *old_table) +{ + st_table *new_table; + st_table_entry *ptr, *entry; + int i, num_bins = old_table->num_bins; + + new_table = (st_table *) avro_new(st_table); + if (new_table == 0) { + return 0; + } + + *new_table = *old_table; + new_table->bins = (st_table_entry **) + Calloc((unsigned)num_bins, sizeof(st_table_entry *)); + + if (new_table->bins == 0) { + avro_freet(st_table, new_table); + return 0; + } + + for (i = 0; i < num_bins; i++) { + new_table->bins[i] = 0; + ptr = old_table->bins[i]; + while (ptr != 0) { + entry = (st_table_entry *) avro_new(st_table_entry); + if (entry == 0) { + free_bins(new_table); + avro_freet(st_table, new_table); + return 0; + } + *entry = *ptr; + entry->next = new_table->bins[i]; + new_table->bins[i] = entry; + ptr = ptr->next; + } + } + return new_table; +} + +int st_delete(register st_table *table,register st_data_t *key,st_data_t *value) +{ + unsigned int hash_val; + st_table_entry *tmp; + register st_table_entry *ptr; + + hash_val = do_hash_bin(*key, table); + ptr = table->bins[hash_val]; + + if (ptr == 0) { + if (value != 0) + *value = 0; + return 0; + } + + if (EQUAL(table, *key, ptr->key)) { + table->bins[hash_val] = ptr->next; + table->num_entries--; + if (value != 0) + *value = ptr->record; + *key = ptr->key; + avro_freet(st_table_entry, ptr); + return 1; + } + + for (; ptr->next != 0; ptr = ptr->next) { + if (EQUAL(table, ptr->next->key, *key)) { + tmp = ptr->next; + ptr->next = ptr->next->next; + table->num_entries--; + if (value != 0) + *value = tmp->record; + *key = tmp->key; + avro_freet(st_table_entry, tmp); + return 1; + } + } + + return 0; +} + +int st_delete_safe(register st_table *table,register st_data_t *key,st_data_t *value,st_data_t never) +{ + unsigned int hash_val; + register st_table_entry *ptr; + + hash_val = do_hash_bin(*key, table); + ptr = table->bins[hash_val]; + + if (ptr == 0) { + if (value != 0) + *value = 0; + return 0; + } + + for (; ptr != 0; ptr = ptr->next) { + if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) { + table->num_entries--; + *key = ptr->key; + if (value != 0) + *value = ptr->record; + ptr->key = ptr->record = never; + return 1; + } + } + + return 0; +} + +static int delete_never(st_data_t key, st_data_t value, st_data_t never) +{ + AVRO_UNUSED(key); + + if (value == never) + return ST_DELETE; + return ST_CONTINUE; +} + +void st_cleanup_safe(st_table *table,st_data_t never) +{ + int num_entries = table->num_entries; + + st_foreach(table, HASH_FUNCTION_CAST delete_never, never); + table->num_entries = num_entries; +} + +int st_foreach(st_table *table,int (*func) (ANYARGS),st_data_t arg) +{ + st_table_entry *ptr, *last, *tmp; + enum st_retval retval; + int i; + + for (i = 0; i < table->num_bins; i++) { + last = 0; + for (ptr = table->bins[i]; ptr != 0;) { + retval = (enum st_retval) (*func) (ptr->key, ptr->record, arg); + switch (retval) { + case ST_CHECK: /* check if hash is modified during + * iteration */ + tmp = 0; + if (i < table->num_bins) { + for (tmp = table->bins[i]; tmp; + tmp = tmp->next) { + if (tmp == ptr) + break; + } + } + if (!tmp) { + /* + * call func with error notice + */ + return 1; + } + /* + * fall through + */ + case ST_CONTINUE: + last = ptr; + ptr = ptr->next; + break; + case ST_STOP: + return 0; + case ST_DELETE: + tmp = ptr; + if (last == 0) { + table->bins[i] = ptr->next; + } else { + last->next = ptr->next; + } + ptr = ptr->next; + avro_freet(st_table_entry, tmp); + table->num_entries--; + } + } + } + return 0; +} + +static int strhash(register const char *string) +{ + register int c; + +#ifdef HASH_ELFHASH + register unsigned int h = 0, g; + + while ((c = *string++) != '\0') { + h = (h << 4) + c; + if (g = h & 0xF0000000) + h ^= g >> 24; + h &= ~g; + } + return h; +#elif defined(HASH_PERL) + register int val = 0; + + while ((c = *string++) != '\0') { + val += c; + val += (val << 10); + val ^= (val >> 6); + } + val += (val << 3); + val ^= (val >> 11); + + return val + (val << 15); +#else + register int val = 0; + + while ((c = *string++) != '\0') { + val = val * 997 + c; + } + + return val + (val >> 5); +#endif +} + +static int numcmp(long x, long y) +{ + return x != y; +} + +static int numhash(long n) +{ + return n; +} diff --git a/fluent-bit/lib/avro/src/st.h b/fluent-bit/lib/avro/src/st.h new file mode 100644 index 000000000..cf8a22491 --- /dev/null +++ b/fluent-bit/lib/avro/src/st.h @@ -0,0 +1,87 @@ +/* + * This is a public domain general purpose hash table package written by + * Peter Moore @ UCB. + */ + +/* + * @(#) st.h 5.1 89/12/14 + */ + +#ifndef ST_INCLUDED +#define ST_INCLUDED +#ifdef __cplusplus +extern "C" { +#define CLOSE_EXTERN } +#else +#define CLOSE_EXTERN +#endif + +#include <avro/platform.h> /* for uintptr_t */ + +#pragma GCC visibility push(hidden) + +#ifndef ANYARGS + #ifdef __cplusplus + #define ANYARGS ... + #else + #define ANYARGS + #endif +#endif + +#ifdef _WIN32 + #define HASH_FUNCTION_CAST (int (__cdecl *)(ANYARGS)) +#else + #define HASH_FUNCTION_CAST +#endif + +typedef uintptr_t st_data_t; +typedef struct st_table st_table; + +struct st_hash_type { + int (*compare) (ANYARGS); + int (*hash) (ANYARGS); +}; + +struct st_table { + struct st_hash_type *type; + int num_bins; + int num_entries; + struct st_table_entry **bins; +}; + +#define st_is_member(table,key) st_lookup(table,key,(st_data_t *)0) + +enum st_retval { ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK }; + +#ifndef _ +# define _(args) args +#endif + +st_table *st_init_table _((struct st_hash_type *)); +st_table *st_init_table_with_size _((struct st_hash_type *, int)); +st_table *st_init_numtable _((void)); +st_table *st_init_numtable_with_size _((int)); +st_table *st_init_strtable _((void)); +st_table *st_init_strtable_with_size _((int)); +int st_delete _((st_table *, st_data_t *, st_data_t *)); +int st_delete_safe _((st_table *, st_data_t *, st_data_t *, st_data_t)); +int st_insert _((st_table *, st_data_t, st_data_t)); +int st_lookup _((st_table *, st_data_t, st_data_t *)); +int st_foreach _((st_table *, int (*)(ANYARGS), st_data_t)); +void st_add_direct _((st_table *, st_data_t, st_data_t)); +void st_free_table _((st_table *)); +void st_cleanup_safe _((st_table *, st_data_t)); +st_table *st_copy _((st_table *)); + +#define ST_NUMCMP ((int (*)()) 0) +#define ST_NUMHASH ((int (*)()) -2) + +#define st_numcmp ST_NUMCMP +#define st_numhash ST_NUMHASH + +int st_strhash(); + +#pragma GCC visibility pop + +CLOSE_EXTERN +#endif /* ST_INCLUDED */ diff --git a/fluent-bit/lib/avro/src/string.c b/fluent-bit/lib/avro/src/string.c new file mode 100644 index 000000000..f5cde949e --- /dev/null +++ b/fluent-bit/lib/avro/src/string.c @@ -0,0 +1,304 @@ +/* + * 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 <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "avro_private.h" +#include "avro/data.h" +#include "avro/allocation.h" +#include "avro/errors.h" + +#ifndef AVRO_STRING_DEBUG +#define AVRO_STRING_DEBUG 0 +#endif + +#if AVRO_STRING_DEBUG +#include <stdio.h> +#define DEBUG(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (0) +#else +#define DEBUG(...) /* don't print messages */ +#endif + + +/* + * A resizable wrapped buffer implementation. This implementation makes + * actual copies in its copy method; if we wanted a zero-copy solution + * here, then we'd have to keep track of all copies of the buffer, so + * that we can update pointers whenever the buffer is resized (since + * this might change the location of the memory region). + */ + +struct avro_wrapped_resizable { + size_t buf_size; +}; + +#define avro_wrapped_resizable_size(sz) \ + (sizeof(struct avro_wrapped_resizable) + (sz)) + +static void +avro_wrapped_resizable_free(avro_wrapped_buffer_t *self) +{ + DEBUG("--- Freeing resizable <%p:%" PRIsz "> (%p)", self->buf, self->size, self->user_data); + struct avro_wrapped_resizable *resizable = (struct avro_wrapped_resizable *) self->user_data; + avro_free(resizable, avro_wrapped_resizable_size(resizable->buf_size)); +} + +static int +avro_wrapped_resizable_resize(avro_wrapped_buffer_t *self, size_t desired) +{ + struct avro_wrapped_resizable *resizable = (struct avro_wrapped_resizable *) self->user_data; + + /* + * If we've already allocated enough memory for the desired + * size, there's nothing to do. + */ + + if (resizable->buf_size >= desired) { + return 0; + } + + size_t new_buf_size = resizable->buf_size * 2; + if (desired > new_buf_size) { + new_buf_size = desired; + } + + DEBUG("--- Resizing <%p:%" PRIsz "> (%p) -> %" PRIsz, + self->buf, self->buf_size, self->user_data, new_buf_size); + + struct avro_wrapped_resizable *new_resizable = + (struct avro_wrapped_resizable *) avro_realloc(resizable, + avro_wrapped_resizable_size(resizable->buf_size), + avro_wrapped_resizable_size(new_buf_size)); + if (new_resizable == NULL) { + return ENOMEM; + } + DEBUG("--- New buffer <%p:%" PRIsz ">", new_buf, new_buf_size); + + new_resizable->buf_size = new_buf_size; + + char *old_buf = (char *) resizable; + char *new_buf = (char *) new_resizable; + + ptrdiff_t offset = (char *) self->buf - old_buf; + DEBUG("--- Old data pointer is %p", self->buf); + self->buf = new_buf + offset; + self->user_data = new_resizable; + DEBUG("--- New data pointer is %p", self->buf); + return 0; +} + +static int +avro_wrapped_resizable_new(avro_wrapped_buffer_t *dest, size_t buf_size) +{ + size_t allocated_size = avro_wrapped_resizable_size(buf_size); + struct avro_wrapped_resizable *resizable = + (struct avro_wrapped_resizable *) avro_malloc(allocated_size); + if (resizable == NULL) { + return ENOMEM; + } + + resizable->buf_size = buf_size; + + dest->buf = ((char *) resizable) + sizeof(struct avro_wrapped_resizable); + DEBUG("--- Creating resizable <%p:%" PRIsz "> (%p)", dest->buf, buf_size, resizable); + dest->size = buf_size; + dest->user_data = resizable; + dest->free = avro_wrapped_resizable_free; + dest->copy = NULL; + dest->slice = NULL; + return 0; +} + +#define is_resizable(buf) \ + ((buf).free == avro_wrapped_resizable_free) + + + +void +avro_raw_string_init(avro_raw_string_t *str) +{ + memset(str, 0, sizeof(avro_raw_string_t)); +} + + +void +avro_raw_string_clear(avro_raw_string_t *str) +{ + /* + * If the string's buffer is one that we control, then we don't + * free it; that lets us reuse the storage on the next call to + * avro_raw_string_set[_length]. + */ + + if (is_resizable(str->wrapped)) { + DEBUG("--- Clearing resizable buffer"); + str->wrapped.size = 0; + } else { + DEBUG("--- Freeing wrapped buffer"); + avro_wrapped_buffer_free(&str->wrapped); + avro_raw_string_init(str); + } +} + + +void +avro_raw_string_done(avro_raw_string_t *str) +{ + avro_wrapped_buffer_free(&str->wrapped); + avro_raw_string_init(str); +} + + +/** + * Makes sure that the string's buffer is one that we allocated + * ourselves, and that the buffer is big enough to hold a string of the + * given length. + */ + +static int +avro_raw_string_ensure_buf(avro_raw_string_t *str, size_t length) +{ + int rval; + + DEBUG("--- Ensuring resizable buffer of size %" PRIsz, length); + if (is_resizable(str->wrapped)) { + /* + * If we've already got a resizable buffer, just have it + * resize itself. + */ + + return avro_wrapped_resizable_resize(&str->wrapped, length); + } else { + /* + * Stash a copy of the old wrapped buffer, and then + * create a new resizable buffer to store our content + * in. + */ + + avro_wrapped_buffer_t orig = str->wrapped; + check(rval, avro_wrapped_resizable_new(&str->wrapped, length)); + + /* + * If there was any content in the old wrapped buffer, + * copy it into the new resizable one. + */ + + if (orig.size > 0) { + size_t to_copy = + (orig.size < length)? orig.size: length; + memcpy((void *) str->wrapped.buf, orig.buf, to_copy); + } + avro_wrapped_buffer_free(&orig); + + return 0; + } +} + + +void +avro_raw_string_set_length(avro_raw_string_t *str, + const void *src, size_t length) +{ + avro_raw_string_ensure_buf(str, length+1); + memcpy((void *) str->wrapped.buf, src, length); + ((char *) str->wrapped.buf)[length] = '\0'; + str->wrapped.size = length; +} + + +void avro_raw_string_append_length(avro_raw_string_t *str, + const void *src, + size_t length) +{ + if (avro_raw_string_length(str) == 0) { + return avro_raw_string_set_length(str, src, length); + } + + avro_raw_string_ensure_buf(str, str->wrapped.size + length); + memcpy((char *) str->wrapped.buf + str->wrapped.size, src, length); + str->wrapped.size += length; +} + + +void +avro_raw_string_set(avro_raw_string_t *str, const char *src) +{ + size_t length = strlen(src); + avro_raw_string_ensure_buf(str, length+1); + memcpy((void *) str->wrapped.buf, src, length+1); + str->wrapped.size = length+1; +} + + +void +avro_raw_string_append(avro_raw_string_t *str, const char *src) +{ + if (avro_raw_string_length(str) == 0) { + return avro_raw_string_set(str, src); + } + + /* Assume that str->wrapped.size includes a NUL terminator */ + size_t length = strlen(src); + avro_raw_string_ensure_buf(str, str->wrapped.size + length); + memcpy((char *) str->wrapped.buf + str->wrapped.size - 1, src, length+1); + str->wrapped.size += length; +} + + +void +avro_raw_string_give(avro_raw_string_t *str, + avro_wrapped_buffer_t *src) +{ + DEBUG("--- Giving control of <%p:%" PRIsz "> (%p) to string", + src->buf, src->size, src); + avro_wrapped_buffer_free(&str->wrapped); + avro_wrapped_buffer_move(&str->wrapped, src); +} + +int +avro_raw_string_grab(const avro_raw_string_t *str, + avro_wrapped_buffer_t *dest) +{ + return avro_wrapped_buffer_copy(dest, &str->wrapped, 0, str->wrapped.size); +} + + +int +avro_raw_string_equals(const avro_raw_string_t *str1, + const avro_raw_string_t *str2) +{ + if (str1 == str2) { + return 1; + } + + if (!str1 || !str2) { + return 0; + } + + if (str1->wrapped.size != str2->wrapped.size) { + return 0; + } + + return (memcmp(str1->wrapped.buf, str2->wrapped.buf, + str1->wrapped.size) == 0); +} diff --git a/fluent-bit/lib/avro/src/value-hash.c b/fluent-bit/lib/avro/src/value-hash.c new file mode 100644 index 000000000..c717924be --- /dev/null +++ b/fluent-bit/lib/avro/src/value-hash.c @@ -0,0 +1,294 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro/value.h" +#include "avro_private.h" + +#define check_return(retval, call) \ + do { \ + int rval = call; \ + if (rval != 0) { return (retval); } \ + } while (0) + +/* + * We currently use MurmurHash3 [1], which is public domain, as our hash + * implementation. + * + * [1] https://code.google.com/p/smhasher/ + */ + +/* Our seed is the MurmurHash3 of the string "avro.value" */ +#define SEED 0xaf4c78df + +#define ROTL32(a,b) (((a) << ((b) & 0x1f)) | ((a) >> (32 - ((b) & 0x1f)))) + +static inline uint32_t +fmix(uint32_t h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + +static const uint32_t c1 = 0xcc9e2d51; +static const uint32_t c2 = 0x1b873593; + +static inline uint32_t +add_hash(uint32_t start, uint32_t current) +{ + current *= c1; + current = ROTL32(current, 15); + current *= c2; + + start ^= current; + start = ROTL32(start, 13); + start = start * 5 + 0xe6546b64; + + return start; +} + +static inline uint32_t +hash_buffer(uint32_t start, const void *src, size_t len) +{ + const uint8_t *data = (const uint8_t *) src; + const int nblocks = len / 4; + + uint32_t h1 = start; + + //---------- + // body + + const uint32_t *blocks = (const uint32_t *) (data + nblocks*4); + int i; + + for (i = -nblocks; i != 0; i++) { + uint32_t k1 = blocks[i]; + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t *tail = (const uint8_t *) (data + nblocks*4); + + uint32_t k1 = 0; + + switch (len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + return h1; +} + +static uint32_t +avro_value_hash_fast(avro_value_t *value, uint32_t start) +{ + avro_type_t type = avro_value_get_type(value); + + switch (type) { + case AVRO_BOOLEAN: + { + int v; + check_return(0, avro_value_get_boolean(value, &v)); + return add_hash(start, v); + } + + case AVRO_BYTES: + { + const void *buf; + size_t size; + check_return(0, avro_value_get_bytes(value, &buf, &size)); + return hash_buffer(start, buf, size); + } + + case AVRO_DOUBLE: + { + union { + double d; + uint32_t u32[2]; + } v; + check_return(0, avro_value_get_double(value, &v.d)); + return add_hash(add_hash(start, v.u32[0]), v.u32[1]); + } + + case AVRO_FLOAT: + { + union { + float f; + uint32_t u32; + } v; + check_return(0, avro_value_get_float(value, &v.f)); + return add_hash(start, v.u32); + } + + case AVRO_INT32: + { + int32_t v; + check_return(0, avro_value_get_int(value, &v)); + return add_hash(start, v); + } + + case AVRO_INT64: + { + union { + int64_t u64; + uint32_t u32[2]; + } v; + check_return(0, avro_value_get_long(value, &v.u64)); + return add_hash(add_hash(start, v.u32[0]), v.u32[1]); + } + + case AVRO_NULL: + { + check_return(0, avro_value_get_null(value)); + return add_hash(start, 0); + } + + case AVRO_STRING: + { + const char *buf; + size_t size; + check_return(0, avro_value_get_string(value, &buf, &size)); + return hash_buffer(start, buf, size); + } + + case AVRO_ARRAY: + { + size_t count; + size_t i; + check_return(0, avro_value_get_size(value, &count)); + + for (i = 0; i < count; i++) { + avro_value_t child; + check_return(0, avro_value_get_by_index + (value, i, &child, NULL)); + start = avro_value_hash_fast(&child, start); + } + + start ^= count; + return start; + } + + case AVRO_ENUM: + { + int v; + check_return(0, avro_value_get_enum(value, &v)); + return add_hash(start, v); + } + + case AVRO_FIXED: + { + const void *buf; + size_t size; + check_return(0, avro_value_get_fixed(value, &buf, &size)); + return hash_buffer(start, buf, size); + } + + case AVRO_MAP: + { + size_t count; + size_t i; + check_return(0, avro_value_get_size(value, &count)); + + /* + * The hash for a map must be built up without + * taking into account the order of the elements + */ + uint32_t map_hash = 0; + for (i = 0; i < count; i++) { + avro_value_t child; + const char *key; + check_return(0, avro_value_get_by_index + (value, i, &child, &key)); + + uint32_t element = SEED; + element = hash_buffer(element, key, strlen(key)); + element = avro_value_hash_fast(&child, element); + element = fmix(element); + + map_hash ^= element; + } + map_hash ^= count; + + return add_hash(start, map_hash); + } + + case AVRO_RECORD: + { + size_t count; + size_t i; + check_return(0, avro_value_get_size(value, &count)); + + for (i = 0; i < count; i++) { + avro_value_t child; + check_return(0, avro_value_get_by_index + (value, i, &child, NULL)); + start = avro_value_hash_fast(&child, start); + } + + start ^= count; + return start; + } + + case AVRO_UNION: + { + int disc; + avro_value_t branch; + check_return(0, avro_value_get_discriminant(value, &disc)); + check_return(0, avro_value_get_current_branch(value, &branch)); + + start = add_hash(start, disc); + start = avro_value_hash_fast(&branch, start); + return start; + } + + default: + return 0; + } +} + +uint32_t +avro_value_hash(avro_value_t *value) +{ + uint32_t hash = avro_value_hash_fast(value, SEED); + return (hash == 0)? hash: fmix(hash); +} diff --git a/fluent-bit/lib/avro/src/value-json.c b/fluent-bit/lib/avro/src/value-json.c new file mode 100644 index 000000000..53c2b3d3e --- /dev/null +++ b/fluent-bit/lib/avro/src/value-json.c @@ -0,0 +1,417 @@ +/* + * 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 <errno.h> +#include <avro/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/errors.h" +#include "avro/legacy.h" +#include "avro/schema.h" +#include "avro/value.h" +#include "avro_private.h" +#include "jansson.h" + +/* + * Converts a binary buffer into a NUL-terminated JSON UTF-8 string. + * Avro bytes and fixed values are encoded in JSON as a string, and JSON + * strings must be in UTF-8. For these Avro types, the JSON string is + * restricted to the characters U+0000..U+00FF, which corresponds to the + * ISO-8859-1 character set. This function performs this conversion. + * The resulting string must be freed using avro_free when you're done + * with it. + */ + +static int +encode_utf8_bytes(const void *src, size_t src_len, + void **dest, size_t *dest_len) +{ + check_param(EINVAL, src, "source"); + check_param(EINVAL, dest, "dest"); + check_param(EINVAL, dest_len, "dest_len"); + + // First, determine the size of the resulting UTF-8 buffer. + // Bytes in the range 0x00..0x7f will take up one byte; bytes in + // the range 0x80..0xff will take up two. + const uint8_t *src8 = (const uint8_t *) src; + + size_t utf8_len = src_len + 1; // +1 for NUL terminator + size_t i; + for (i = 0; i < src_len; i++) { + if (src8[i] & 0x80) { + utf8_len++; + } + } + + // Allocate a new buffer for the UTF-8 string and fill it in. + uint8_t *dest8 = (uint8_t *) avro_malloc(utf8_len); + if (dest8 == NULL) { + avro_set_error("Cannot allocate JSON bytes buffer"); + return ENOMEM; + } + + uint8_t *curr = dest8; + for (i = 0; i < src_len; i++) { + if (src8[i] & 0x80) { + *curr++ = (0xc0 | (src8[i] >> 6)); + *curr++ = (0x80 | (src8[i] & 0x3f)); + } else { + *curr++ = src8[i]; + } + } + + *curr = '\0'; + + // And we're good. + *dest = dest8; + *dest_len = utf8_len; + return 0; +} + +#define return_json(type, exp) \ + { \ + json_t *result = exp; \ + if (result == NULL) { \ + avro_set_error("Cannot allocate JSON " type); \ + } \ + return result; \ + } + +#define check_return(retval, call) \ + do { \ + int __rc; \ + __rc = call; \ + if (__rc != 0) { \ + return retval; \ + } \ + } while (0) + +static json_t * +avro_value_to_json_t(const avro_value_t *value) +{ + switch (avro_value_get_type(value)) { + case AVRO_BOOLEAN: + { + int val; + check_return(NULL, avro_value_get_boolean(value, &val)); + return_json("boolean", + val? json_true(): json_false()); + } + + case AVRO_BYTES: + { + const void *val; + size_t size; + void *encoded = NULL; + size_t encoded_size = 0; + + check_return(NULL, avro_value_get_bytes(value, &val, &size)); + + if (encode_utf8_bytes(val, size, &encoded, &encoded_size)) { + return NULL; + } + + json_t *result = json_string_nocheck((const char *) encoded); + avro_free(encoded, encoded_size); + if (result == NULL) { + avro_set_error("Cannot allocate JSON bytes"); + } + return result; + } + + case AVRO_DOUBLE: + { + double val; + check_return(NULL, avro_value_get_double(value, &val)); + return_json("double", json_real(val)); + } + + case AVRO_FLOAT: + { + float val; + check_return(NULL, avro_value_get_float(value, &val)); + return_json("float", json_real(val)); + } + + case AVRO_INT32: + { + int32_t val; + check_return(NULL, avro_value_get_int(value, &val)); + return_json("int", json_integer(val)); + } + + case AVRO_INT64: + { + int64_t val; + check_return(NULL, avro_value_get_long(value, &val)); + return_json("long", json_integer(val)); + } + + case AVRO_NULL: + { + check_return(NULL, avro_value_get_null(value)); + return_json("null", json_null()); + } + + case AVRO_STRING: + { + const char *val; + size_t size; + check_return(NULL, avro_value_get_string(value, &val, &size)); + return_json("string", json_string(val)); + } + + case AVRO_ARRAY: + { + int rc; + size_t element_count, i; + json_t *result = json_array(); + if (result == NULL) { + avro_set_error("Cannot allocate JSON array"); + return NULL; + } + + rc = avro_value_get_size(value, &element_count); + if (rc != 0) { + json_decref(result); + return NULL; + } + + for (i = 0; i < element_count; i++) { + avro_value_t element; + rc = avro_value_get_by_index(value, i, &element, NULL); + if (rc != 0) { + json_decref(result); + return NULL; + } + + json_t *element_json = avro_value_to_json_t(&element); + if (element_json == NULL) { + json_decref(result); + return NULL; + } + + if (json_array_append_new(result, element_json)) { + avro_set_error("Cannot append element to array"); + json_decref(result); + return NULL; + } + } + + return result; + } + + case AVRO_ENUM: + { + avro_schema_t enum_schema; + int symbol_value; + const char *symbol_name; + + check_return(NULL, avro_value_get_enum(value, &symbol_value)); + enum_schema = avro_value_get_schema(value); + symbol_name = avro_schema_enum_get(enum_schema, symbol_value); + return_json("enum", json_string(symbol_name)); + } + + case AVRO_FIXED: + { + const void *val; + size_t size; + void *encoded = NULL; + size_t encoded_size = 0; + + check_return(NULL, avro_value_get_fixed(value, &val, &size)); + + if (encode_utf8_bytes(val, size, &encoded, &encoded_size)) { + return NULL; + } + + json_t *result = json_string_nocheck((const char *) encoded); + avro_free(encoded, encoded_size); + if (result == NULL) { + avro_set_error("Cannot allocate JSON fixed"); + } + return result; + } + + case AVRO_MAP: + { + int rc; + size_t element_count, i; + json_t *result = json_object(); + if (result == NULL) { + avro_set_error("Cannot allocate JSON map"); + return NULL; + } + + rc = avro_value_get_size(value, &element_count); + if (rc != 0) { + json_decref(result); + return NULL; + } + + for (i = 0; i < element_count; i++) { + const char *key; + avro_value_t element; + + rc = avro_value_get_by_index(value, i, &element, &key); + if (rc != 0) { + json_decref(result); + return NULL; + } + + json_t *element_json = avro_value_to_json_t(&element); + if (element_json == NULL) { + json_decref(result); + return NULL; + } + + if (json_object_set_new(result, key, element_json)) { + avro_set_error("Cannot append element to map"); + json_decref(result); + return NULL; + } + } + + return result; + } + + case AVRO_RECORD: + { + int rc; + size_t field_count, i; + json_t *result = json_object(); + if (result == NULL) { + avro_set_error("Cannot allocate new JSON record"); + return NULL; + } + + rc = avro_value_get_size(value, &field_count); + if (rc != 0) { + json_decref(result); + return NULL; + } + + for (i = 0; i < field_count; i++) { + const char *field_name; + avro_value_t field; + + rc = avro_value_get_by_index(value, i, &field, &field_name); + if (rc != 0) { + json_decref(result); + return NULL; + } + + json_t *field_json = avro_value_to_json_t(&field); + if (field_json == NULL) { + json_decref(result); + return NULL; + } + + if (json_object_set_new(result, field_name, field_json)) { + avro_set_error("Cannot append field to record"); + json_decref(result); + return NULL; + } + } + + return result; + } + + case AVRO_UNION: + { + int disc; + avro_value_t branch; + avro_schema_t union_schema; + avro_schema_t branch_schema; + const char *branch_name; + + check_return(NULL, avro_value_get_current_branch(value, &branch)); + + if (avro_value_get_type(&branch) == AVRO_NULL) { + return_json("null", json_null()); + } + + check_return(NULL, avro_value_get_discriminant(value, &disc)); + union_schema = avro_value_get_schema(value); + branch_schema = + avro_schema_union_branch(union_schema, disc); + branch_name = avro_schema_type_name(branch_schema); + + json_t *result = json_object(); + if (result == NULL) { + avro_set_error("Cannot allocate JSON union"); + return NULL; + } + + json_t *branch_json = avro_value_to_json_t(&branch); + if (branch_json == NULL) { + json_decref(result); + return NULL; + } + + if (json_object_set_new(result, branch_name, branch_json)) { + avro_set_error("Cannot append branch to union"); + json_decref(result); + return NULL; + } + + return result; + } + + default: + return NULL; + } +} + +int +avro_value_to_json(const avro_value_t *value, + int one_line, char **json_str) +{ + check_param(EINVAL, value, "value"); + check_param(EINVAL, json_str, "string buffer"); + + json_t *json = avro_value_to_json_t(value); + if (json == NULL) { + return ENOMEM; + } + + /* + * Jansson will only encode an object or array as the root + * element. + */ + + *json_str = json_dumps + (json, + JSON_ENCODE_ANY | + JSON_INDENT(one_line? 0: 2) | + JSON_ENSURE_ASCII | + JSON_PRESERVE_ORDER); + json_decref(json); + return 0; +} + +int +avro_datum_to_json(const avro_datum_t datum, + int one_line, char **json_str) +{ + avro_value_t value; + avro_datum_as_value(&value, datum); + return avro_value_to_json(&value, one_line, json_str); +} diff --git a/fluent-bit/lib/avro/src/value-read.c b/fluent-bit/lib/avro/src/value-read.c new file mode 100644 index 000000000..b6b6e79fa --- /dev/null +++ b/fluent-bit/lib/avro/src/value-read.c @@ -0,0 +1,392 @@ +/* + * 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/platform.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/basics.h" +#include "avro/data.h" +#include "avro/io.h" +#include "avro/value.h" +#include "avro_private.h" +#include "encoding.h" + + +/* + * Forward declaration; this is basically the same as avro_value_read, + * but it doesn't reset dest first. (Since it will have already been + * reset in avro_value_read itself). + */ + +static int +read_value(avro_reader_t reader, avro_value_t *dest); + + +static int +read_array_value(avro_reader_t reader, avro_value_t *dest) +{ + int rval; + size_t i; /* index within the current block */ + size_t index = 0; /* index within the entire array */ + int64_t block_count; + int64_t block_size; + + check_prefix(rval, avro_binary_encoding. + read_long(reader, &block_count), + "Cannot read array block count: "); + + while (block_count != 0) { + if (block_count < 0) { + block_count = block_count * -1; + check_prefix(rval, avro_binary_encoding. + read_long(reader, &block_size), + "Cannot read array block size: "); + } + + for (i = 0; i < (size_t) block_count; i++, index++) { + avro_value_t child; + + check(rval, avro_value_append(dest, &child, NULL)); + check(rval, read_value(reader, &child)); + } + + check_prefix(rval, avro_binary_encoding. + read_long(reader, &block_count), + "Cannot read array block count: "); + } + + return 0; +} + + +static int +read_map_value(avro_reader_t reader, avro_value_t *dest) +{ + int rval; + size_t i; /* index within the current block */ + size_t index = 0; /* index within the entire array */ + int64_t block_count; + int64_t block_size; + + check_prefix(rval, avro_binary_encoding.read_long(reader, &block_count), + "Cannot read map block count: "); + + while (block_count != 0) { + if (block_count < 0) { + block_count = block_count * -1; + check_prefix(rval, avro_binary_encoding. + read_long(reader, &block_size), + "Cannot read map block size: "); + } + + for (i = 0; i < (size_t) block_count; i++, index++) { + char *key; + int64_t key_size; + avro_value_t child; + + check_prefix(rval, avro_binary_encoding. + read_string(reader, &key, &key_size), + "Cannot read map key: "); + + rval = avro_value_add(dest, key, &child, NULL, NULL); + if (rval) { + avro_free(key, key_size); + return rval; + } + + rval = read_value(reader, &child); + if (rval) { + avro_free(key, key_size); + return rval; + } + + avro_free(key, key_size); + } + + check_prefix(rval, avro_binary_encoding. + read_long(reader, &block_count), + "Cannot read map block count: "); + } + + return 0; +} + + +static int +read_record_value(avro_reader_t reader, avro_value_t *dest) +{ + int rval; + size_t field_count; + size_t i; + + avro_schema_t record_schema = avro_value_get_schema(dest); + + check(rval, avro_value_get_size(dest, &field_count)); + for (i = 0; i < field_count; i++) { + avro_value_t field; + + check(rval, avro_value_get_by_index(dest, i, &field, NULL)); + if (field.iface != NULL) { + check(rval, read_value(reader, &field)); + } else { + avro_schema_t field_schema = + avro_schema_record_field_get_by_index(record_schema, i); + check(rval, avro_skip_data(reader, field_schema)); + } + } + + return 0; +} + + +static int +read_union_value(avro_reader_t reader, avro_value_t *dest) +{ + int rval; + int64_t discriminant; + avro_schema_t union_schema; + int64_t branch_count; + avro_value_t branch; + + check_prefix(rval, avro_binary_encoding. + read_long(reader, &discriminant), + "Cannot read union discriminant: "); + + union_schema = avro_value_get_schema(dest); + branch_count = avro_schema_union_size(union_schema); + + if (discriminant < 0 || discriminant >= branch_count) { + avro_set_error("Invalid union discriminant value: (%d)", + discriminant); + return 1; + } + + check(rval, avro_value_set_branch(dest, discriminant, &branch)); + check(rval, read_value(reader, &branch)); + return 0; +} + + +/* + * A wrapped buffer implementation that takes control of a buffer + * allocated using avro_malloc. + */ + +struct avro_wrapped_alloc { + const void *original; + size_t allocated_size; +}; + +static void +avro_wrapped_alloc_free(avro_wrapped_buffer_t *self) +{ + struct avro_wrapped_alloc *alloc = (struct avro_wrapped_alloc *) self->user_data; + avro_free((void *) alloc->original, alloc->allocated_size); + avro_freet(struct avro_wrapped_alloc, alloc); +} + +static int +avro_wrapped_alloc_new(avro_wrapped_buffer_t *dest, + const void *buf, size_t length) +{ + struct avro_wrapped_alloc *alloc = (struct avro_wrapped_alloc *) avro_new(struct avro_wrapped_alloc); + if (alloc == NULL) { + return ENOMEM; + } + + dest->buf = buf; + dest->size = length; + dest->user_data = alloc; + dest->free = avro_wrapped_alloc_free; + dest->copy = NULL; + dest->slice = NULL; + + alloc->original = buf; + alloc->allocated_size = length; + return 0; +} + + +static int +read_value(avro_reader_t reader, avro_value_t *dest) +{ + int rval; + + switch (avro_value_get_type(dest)) { + case AVRO_BOOLEAN: + { + int8_t val; + check_prefix(rval, avro_binary_encoding. + read_boolean(reader, &val), + "Cannot read boolean value: "); + return avro_value_set_boolean(dest, val); + } + + case AVRO_BYTES: + { + char *bytes; + int64_t len; + check_prefix(rval, avro_binary_encoding. + read_bytes(reader, &bytes, &len), + "Cannot read bytes value: "); + + /* + * read_bytes allocates an extra byte to always + * ensure that the data is NUL terminated, but + * that byte isn't included in the length. We + * include that extra byte in the allocated + * size, but not in the length of the buffer. + */ + + avro_wrapped_buffer_t buf; + check(rval, avro_wrapped_alloc_new(&buf, bytes, len+1)); + buf.size--; + return avro_value_give_bytes(dest, &buf); + } + + case AVRO_DOUBLE: + { + double val; + check_prefix(rval, avro_binary_encoding. + read_double(reader, &val), + "Cannot read double value: "); + return avro_value_set_double(dest, val); + } + + case AVRO_FLOAT: + { + float val; + check_prefix(rval, avro_binary_encoding. + read_float(reader, &val), + "Cannot read float value: "); + return avro_value_set_float(dest, val); + } + + case AVRO_INT32: + { + int32_t val; + check_prefix(rval, avro_binary_encoding. + read_int(reader, &val), + "Cannot read int value: "); + return avro_value_set_int(dest, val); + } + + case AVRO_INT64: + { + int64_t val; + check_prefix(rval, avro_binary_encoding. + read_long(reader, &val), + "Cannot read long value: "); + return avro_value_set_long(dest, val); + } + + case AVRO_NULL: + { + check_prefix(rval, avro_binary_encoding. + read_null(reader), + "Cannot read null value: "); + return avro_value_set_null(dest); + } + + case AVRO_STRING: + { + char *str; + int64_t size; + + /* + * read_string returns a size that includes the + * NUL terminator, and the free function will be + * called with a size that also includes the NUL + */ + + check_prefix(rval, avro_binary_encoding. + read_string(reader, &str, &size), + "Cannot read string value: "); + + avro_wrapped_buffer_t buf; + check(rval, avro_wrapped_alloc_new(&buf, str, size)); + return avro_value_give_string_len(dest, &buf); + } + + case AVRO_ARRAY: + return read_array_value(reader, dest); + + case AVRO_ENUM: + { + int64_t val; + check_prefix(rval, avro_binary_encoding. + read_long(reader, &val), + "Cannot read enum value: "); + return avro_value_set_enum(dest, val); + } + + case AVRO_FIXED: + { + avro_schema_t schema = avro_value_get_schema(dest); + char *bytes; + int64_t size = avro_schema_fixed_size(schema); + + bytes = (char *) avro_malloc(size); + if (!bytes) { + avro_prefix_error("Cannot allocate new fixed value"); + return ENOMEM; + } + rval = avro_read(reader, bytes, size); + if (rval) { + avro_prefix_error("Cannot read fixed value: "); + avro_free(bytes, size); + return rval; + } + + avro_wrapped_buffer_t buf; + rval = avro_wrapped_alloc_new(&buf, bytes, size); + if (rval != 0) { + avro_free(bytes, size); + return rval; + } + + return avro_value_give_fixed(dest, &buf); + } + + case AVRO_MAP: + return read_map_value(reader, dest); + + case AVRO_RECORD: + return read_record_value(reader, dest); + + case AVRO_UNION: + return read_union_value(reader, dest); + + default: + { + avro_set_error("Unknown schema type"); + return EINVAL; + } + } + + return 0; +} + +int +avro_value_read(avro_reader_t reader, avro_value_t *dest) +{ + int rval; + check(rval, avro_value_reset(dest)); + return read_value(reader, dest); +} diff --git a/fluent-bit/lib/avro/src/value-sizeof.c b/fluent-bit/lib/avro/src/value-sizeof.c new file mode 100644 index 000000000..bcbffb5b6 --- /dev/null +++ b/fluent-bit/lib/avro/src/value-sizeof.c @@ -0,0 +1,230 @@ +/* + * 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/platform.h> +#include <stdlib.h> + +#include "avro/basics.h" +#include "avro/io.h" +#include "avro/value.h" +#include "avro_private.h" +#include "encoding.h" + + +/* + * Forward declaration; this is basically the same as avro_value_sizeof, + * but it doesn't initialize size first. (Since it will have already + * been initialized in avro_value_sizeof itself). + */ + +static int +sizeof_value(avro_value_t *src, size_t *size); + + +static int +sizeof_array_value(avro_value_t *src, size_t *size) +{ + int rval; + size_t element_count; + check(rval, avro_value_get_size(src, &element_count)); + + if (element_count > 0) { + *size += avro_binary_encoding.size_long(NULL, element_count); + + size_t i; + for (i = 0; i < element_count; i++) { + avro_value_t child; + check(rval, avro_value_get_by_index(src, i, &child, NULL)); + check(rval, sizeof_value(&child, size)); + } + } + + *size += avro_binary_encoding.size_long(NULL, 0); + return 0; +} + + +static int +sizeof_map_value(avro_value_t *src, size_t *size) +{ + int rval; + size_t element_count; + check(rval, avro_value_get_size(src, &element_count)); + + if (element_count > 0) { + *size += avro_binary_encoding.size_long(NULL, element_count); + + size_t i; + for (i = 0; i < element_count; i++) { + avro_value_t child; + const char *key; + check(rval, avro_value_get_by_index(src, i, &child, &key)); + *size += avro_binary_encoding.size_string(NULL, key); + check(rval, sizeof_value(&child, size)); + } + } + + *size += avro_binary_encoding.size_long(NULL, 0); + return 0; +} + +static int +sizeof_record_value(avro_value_t *src, size_t *size) +{ + int rval; + size_t field_count; + check(rval, avro_value_get_size(src, &field_count)); + + size_t i; + for (i = 0; i < field_count; i++) { + avro_value_t field; + check(rval, avro_value_get_by_index(src, i, &field, NULL)); + check(rval, sizeof_value(&field, size)); + } + + return 0; +} + +static int +sizeof_union_value(avro_value_t *src, size_t *size) +{ + int rval; + int discriminant; + avro_value_t branch; + + check(rval, avro_value_get_discriminant(src, &discriminant)); + check(rval, avro_value_get_current_branch(src, &branch)); + *size += avro_binary_encoding.size_long(NULL, discriminant); + return sizeof_value(&branch, size); +} + +static int +sizeof_value(avro_value_t *src, size_t *size) +{ + int rval; + + switch (avro_value_get_type(src)) { + case AVRO_BOOLEAN: + { + int val; + check(rval, avro_value_get_boolean(src, &val)); + *size += avro_binary_encoding.size_boolean(NULL, val); + return 0; + } + + case AVRO_BYTES: + { + const void *buf; + size_t sz; + check(rval, avro_value_get_bytes(src, &buf, &sz)); + *size += avro_binary_encoding.size_bytes(NULL, (const char *) buf, sz); + return 0; + } + + case AVRO_DOUBLE: + { + double val; + check(rval, avro_value_get_double(src, &val)); + *size += avro_binary_encoding.size_double(NULL, val); + return 0; + } + + case AVRO_FLOAT: + { + float val; + check(rval, avro_value_get_float(src, &val)); + *size += avro_binary_encoding.size_float(NULL, val); + return 0; + } + + case AVRO_INT32: + { + int32_t val; + check(rval, avro_value_get_int(src, &val)); + *size += avro_binary_encoding.size_long(NULL, val); + return 0; + } + + case AVRO_INT64: + { + int64_t val; + check(rval, avro_value_get_long(src, &val)); + *size += avro_binary_encoding.size_long(NULL, val); + return 0; + } + + case AVRO_NULL: + { + check(rval, avro_value_get_null(src)); + *size += avro_binary_encoding.size_null(NULL); + return 0; + } + + case AVRO_STRING: + { + const char *str; + size_t sz; + check(rval, avro_value_get_string(src, &str, &sz)); + *size += avro_binary_encoding.size_bytes(NULL, str, sz-1); + return 0; + } + + case AVRO_ARRAY: + return sizeof_array_value(src, size); + + case AVRO_ENUM: + { + int val; + check(rval, avro_value_get_enum(src, &val)); + *size += avro_binary_encoding.size_long(NULL, val); + return 0; + } + + case AVRO_FIXED: + { + size_t sz; + check(rval, avro_value_get_fixed(src, NULL, &sz)); + *size += sz; + return 0; + } + + case AVRO_MAP: + return sizeof_map_value(src, size); + + case AVRO_RECORD: + return sizeof_record_value(src, size); + + case AVRO_UNION: + return sizeof_union_value(src, size); + + default: + { + avro_set_error("Unknown schema type"); + return EINVAL; + } + } + + return 0; +} + +int +avro_value_sizeof(avro_value_t *src, size_t *size) +{ + check_param(EINVAL, size, "size pointer"); + *size = 0; + return sizeof_value(src, size); +} diff --git a/fluent-bit/lib/avro/src/value-write.c b/fluent-bit/lib/avro/src/value-write.c new file mode 100644 index 000000000..bcd0fb0a4 --- /dev/null +++ b/fluent-bit/lib/avro/src/value-write.c @@ -0,0 +1,209 @@ +/* + * 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/platform.h> +#include <stdlib.h> + +#include "avro/basics.h" +#include "avro/io.h" +#include "avro/value.h" +#include "avro_private.h" +#include "encoding.h" + + +static int +write_array_value(avro_writer_t writer, avro_value_t *src) +{ + int rval; + size_t element_count; + check(rval, avro_value_get_size(src, &element_count)); + + if (element_count > 0) { + check_prefix(rval, avro_binary_encoding.write_long + (writer, element_count), + "Cannot write array block count: "); + + size_t i; + for (i = 0; i < element_count; i++) { + avro_value_t child; + check(rval, avro_value_get_by_index(src, i, &child, NULL)); + check(rval, avro_value_write(writer, &child)); + } + } + + check_prefix(rval, avro_binary_encoding.write_long(writer, 0), + "Cannot write array block count: "); + return 0; +} + + +static int +write_map_value(avro_writer_t writer, avro_value_t *src) +{ + int rval; + size_t element_count; + check(rval, avro_value_get_size(src, &element_count)); + + if (element_count > 0) { + check_prefix(rval, avro_binary_encoding.write_long + (writer, element_count), + "Cannot write map block count: "); + + size_t i; + for (i = 0; i < element_count; i++) { + avro_value_t child; + const char *key; + check(rval, avro_value_get_by_index(src, i, &child, &key)); + check(rval, avro_binary_encoding.write_string(writer, key)); + check(rval, avro_value_write(writer, &child)); + } + } + + check_prefix(rval, avro_binary_encoding.write_long(writer, 0), + "Cannot write map block count: "); + return 0; +} + +static int +write_record_value(avro_writer_t writer, avro_value_t *src) +{ + int rval; + size_t field_count; + check(rval, avro_value_get_size(src, &field_count)); + + size_t i; + for (i = 0; i < field_count; i++) { + avro_value_t field; + check(rval, avro_value_get_by_index(src, i, &field, NULL)); + check(rval, avro_value_write(writer, &field)); + } + + return 0; +} + +static int +write_union_value(avro_writer_t writer, avro_value_t *src) +{ + int rval; + int discriminant; + avro_value_t branch; + + check(rval, avro_value_get_discriminant(src, &discriminant)); + check(rval, avro_value_get_current_branch(src, &branch)); + check(rval, avro_binary_encoding.write_long(writer, discriminant)); + return avro_value_write(writer, &branch); +} + +int +avro_value_write(avro_writer_t writer, avro_value_t *src) +{ + int rval; + + switch (avro_value_get_type(src)) { + case AVRO_BOOLEAN: + { + int val; + check(rval, avro_value_get_boolean(src, &val)); + return avro_binary_encoding.write_boolean(writer, val); + } + + case AVRO_BYTES: + { + const void *buf; + size_t size; + check(rval, avro_value_get_bytes(src, &buf, &size)); + return avro_binary_encoding.write_bytes(writer, (const char *) buf, size); + } + + case AVRO_DOUBLE: + { + double val; + check(rval, avro_value_get_double(src, &val)); + return avro_binary_encoding.write_double(writer, val); + } + + case AVRO_FLOAT: + { + float val; + check(rval, avro_value_get_float(src, &val)); + return avro_binary_encoding.write_float(writer, val); + } + + case AVRO_INT32: + { + int32_t val; + check(rval, avro_value_get_int(src, &val)); + return avro_binary_encoding.write_long(writer, val); + } + + case AVRO_INT64: + { + int64_t val; + check(rval, avro_value_get_long(src, &val)); + return avro_binary_encoding.write_long(writer, val); + } + + case AVRO_NULL: + { + check(rval, avro_value_get_null(src)); + return avro_binary_encoding.write_null(writer); + } + + case AVRO_STRING: + { + const char *str; + size_t size; + check(rval, avro_value_get_string(src, &str, &size)); + return avro_binary_encoding.write_bytes(writer, str, size-1); + } + + case AVRO_ARRAY: + return write_array_value(writer, src); + + case AVRO_ENUM: + { + int val; + check(rval, avro_value_get_enum(src, &val)); + return avro_binary_encoding.write_long(writer, val); + } + + case AVRO_FIXED: + { + const void *buf; + size_t size; + check(rval, avro_value_get_fixed(src, &buf, &size)); + return avro_write(writer, (void *) buf, size); + } + + case AVRO_MAP: + return write_map_value(writer, src); + + case AVRO_RECORD: + return write_record_value(writer, src); + + case AVRO_UNION: + return write_union_value(writer, src); + + default: + { + avro_set_error("Unknown schema type"); + return EINVAL; + } + } + + return 0; +} diff --git a/fluent-bit/lib/avro/src/value.c b/fluent-bit/lib/avro/src/value.c new file mode 100644 index 000000000..b177504e5 --- /dev/null +++ b/fluent-bit/lib/avro/src/value.c @@ -0,0 +1,690 @@ +/* + * 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 <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "avro/allocation.h" +#include "avro/data.h" +#include "avro/errors.h" +#include "avro/value.h" +#include "avro_private.h" + + +#define check_return(retval, call) \ + do { \ + int rval = call; \ + if (rval != 0) { return (retval); } \ + } while (0) + + +void +avro_value_incref(avro_value_t *value) +{ + value->iface->incref(value); +} + +void +avro_value_decref(avro_value_t *value) +{ + value->iface->decref(value); + avro_value_iface_decref(value->iface); + value->iface = NULL; + value->self = NULL; +} + +void +avro_value_copy_ref(avro_value_t *dest, const avro_value_t *src) +{ + dest->iface = src->iface; + dest->self = src->self; + avro_value_iface_incref(dest->iface); + dest->iface->incref(dest); +} + +void +avro_value_move_ref(avro_value_t *dest, avro_value_t *src) +{ + dest->iface = src->iface; + dest->self = src->self; + src->iface = NULL; + src->self = NULL; +} + + +int +avro_value_equal_fast(avro_value_t *val1, avro_value_t *val2) +{ + avro_type_t type1 = avro_value_get_type(val1); + avro_type_t type2 = avro_value_get_type(val2); + if (type1 != type2) { + return 0; + } + + switch (type1) { + case AVRO_BOOLEAN: + { + int v1; + int v2; + check_return(0, avro_value_get_boolean(val1, &v1)); + check_return(0, avro_value_get_boolean(val2, &v2)); + return (v1 == v2); + } + + case AVRO_BYTES: + { + const void *buf1; + const void *buf2; + size_t size1; + size_t size2; + check_return(0, avro_value_get_bytes(val1, &buf1, &size1)); + check_return(0, avro_value_get_bytes(val2, &buf2, &size2)); + if (size1 != size2) { + return 0; + } + return (memcmp(buf1, buf2, size1) == 0); + } + + case AVRO_DOUBLE: + { + double v1; + double v2; + check_return(0, avro_value_get_double(val1, &v1)); + check_return(0, avro_value_get_double(val2, &v2)); + return (v1 == v2); + } + + case AVRO_FLOAT: + { + float v1; + float v2; + check_return(0, avro_value_get_float(val1, &v1)); + check_return(0, avro_value_get_float(val2, &v2)); + return (v1 == v2); + } + + case AVRO_INT32: + { + int32_t v1; + int32_t v2; + check_return(0, avro_value_get_int(val1, &v1)); + check_return(0, avro_value_get_int(val2, &v2)); + return (v1 == v2); + } + + case AVRO_INT64: + { + int64_t v1; + int64_t v2; + check_return(0, avro_value_get_long(val1, &v1)); + check_return(0, avro_value_get_long(val2, &v2)); + return (v1 == v2); + } + + case AVRO_NULL: + { + check_return(0, avro_value_get_null(val1)); + check_return(0, avro_value_get_null(val2)); + return 1; + } + + case AVRO_STRING: + { + const char *buf1; + const char *buf2; + size_t size1; + size_t size2; + check_return(0, avro_value_get_string(val1, &buf1, &size1)); + check_return(0, avro_value_get_string(val2, &buf2, &size2)); + if (size1 != size2) { + return 0; + } + return (memcmp(buf1, buf2, size1) == 0); + } + + case AVRO_ARRAY: + { + size_t count1; + size_t count2; + check_return(0, avro_value_get_size(val1, &count1)); + check_return(0, avro_value_get_size(val2, &count2)); + if (count1 != count2) { + return 0; + } + + size_t i; + for (i = 0; i < count1; i++) { + avro_value_t child1; + avro_value_t child2; + check_return(0, avro_value_get_by_index + (val1, i, &child1, NULL)); + check_return(0, avro_value_get_by_index + (val2, i, &child2, NULL)); + if (!avro_value_equal_fast(&child1, &child2)) { + return 0; + } + } + + return 1; + } + + case AVRO_ENUM: + { + int v1; + int v2; + check_return(0, avro_value_get_enum(val1, &v1)); + check_return(0, avro_value_get_enum(val2, &v2)); + return (v1 == v2); + } + + case AVRO_FIXED: + { + const void *buf1; + const void *buf2; + size_t size1; + size_t size2; + check_return(0, avro_value_get_fixed(val1, &buf1, &size1)); + check_return(0, avro_value_get_fixed(val2, &buf2, &size2)); + if (size1 != size2) { + return 0; + } + return (memcmp(buf1, buf2, size1) == 0); + } + + case AVRO_MAP: + { + size_t count1; + size_t count2; + check_return(0, avro_value_get_size(val1, &count1)); + check_return(0, avro_value_get_size(val2, &count2)); + if (count1 != count2) { + return 0; + } + + size_t i; + for (i = 0; i < count1; i++) { + avro_value_t child1; + avro_value_t child2; + const char *key1; + check_return(0, avro_value_get_by_index + (val1, i, &child1, &key1)); + check_return(0, avro_value_get_by_name + (val2, key1, &child2, NULL)); + if (!avro_value_equal_fast(&child1, &child2)) { + return 0; + } + } + + return 1; + } + + case AVRO_RECORD: + { + size_t count1; + check_return(0, avro_value_get_size(val1, &count1)); + + size_t i; + for (i = 0; i < count1; i++) { + avro_value_t child1; + avro_value_t child2; + check_return(0, avro_value_get_by_index + (val1, i, &child1, NULL)); + check_return(0, avro_value_get_by_index + (val2, i, &child2, NULL)); + if (!avro_value_equal_fast(&child1, &child2)) { + return 0; + } + } + + return 1; + } + + case AVRO_UNION: + { + int disc1; + int disc2; + check_return(0, avro_value_get_discriminant(val1, &disc1)); + check_return(0, avro_value_get_discriminant(val2, &disc2)); + if (disc1 != disc2) { + return 0; + } + + avro_value_t branch1; + avro_value_t branch2; + check_return(0, avro_value_get_current_branch(val1, &branch1)); + check_return(0, avro_value_get_current_branch(val2, &branch2)); + return avro_value_equal_fast(&branch1, &branch2); + } + + default: + return 0; + } +} + +int +avro_value_equal(avro_value_t *val1, avro_value_t *val2) +{ + avro_schema_t schema1 = avro_value_get_schema(val1); + avro_schema_t schema2 = avro_value_get_schema(val2); + if (!avro_schema_equal(schema1, schema2)) { + return 0; + } + + return avro_value_equal_fast(val1, val2); +} + + +#define cmp(v1, v2) \ + (((v1) == (v2))? 0: \ + ((v1) < (v2))? -1: 1) +int +avro_value_cmp_fast(avro_value_t *val1, avro_value_t *val2) +{ + avro_type_t type1 = avro_value_get_type(val1); + avro_type_t type2 = avro_value_get_type(val2); + if (type1 != type2) { + return -1; + } + + switch (type1) { + case AVRO_BOOLEAN: + { + int v1; + int v2; + check_return(0, avro_value_get_boolean(val1, &v1)); + check_return(0, avro_value_get_boolean(val2, &v2)); + return cmp(!!v1, !!v2); + } + + case AVRO_BYTES: + { + const void *buf1; + const void *buf2; + size_t size1; + size_t size2; + size_t min_size; + int result; + + check_return(0, avro_value_get_bytes(val1, &buf1, &size1)); + check_return(0, avro_value_get_bytes(val2, &buf2, &size2)); + + min_size = (size1 < size2)? size1: size2; + result = memcmp(buf1, buf2, min_size); + if (result != 0) { + return result; + } else { + return cmp(size1, size2); + } + } + + case AVRO_DOUBLE: + { + double v1; + double v2; + check_return(0, avro_value_get_double(val1, &v1)); + check_return(0, avro_value_get_double(val2, &v2)); + return cmp(v1, v2); + } + + case AVRO_FLOAT: + { + float v1; + float v2; + check_return(0, avro_value_get_float(val1, &v1)); + check_return(0, avro_value_get_float(val2, &v2)); + return cmp(v1, v2); + } + + case AVRO_INT32: + { + int32_t v1; + int32_t v2; + check_return(0, avro_value_get_int(val1, &v1)); + check_return(0, avro_value_get_int(val2, &v2)); + return cmp(v1, v2); + } + + case AVRO_INT64: + { + int64_t v1; + int64_t v2; + check_return(0, avro_value_get_long(val1, &v1)); + check_return(0, avro_value_get_long(val2, &v2)); + return cmp(v1, v2); + } + + case AVRO_NULL: + { + check_return(0, avro_value_get_null(val1)); + check_return(0, avro_value_get_null(val2)); + return 0; + } + + case AVRO_STRING: + { + const char *buf1; + const char *buf2; + size_t size1; + size_t size2; + size_t min_size; + int result; + check_return(0, avro_value_get_string(val1, &buf1, &size1)); + check_return(0, avro_value_get_string(val2, &buf2, &size2)); + + min_size = (size1 < size2)? size1: size2; + result = memcmp(buf1, buf2, min_size); + if (result != 0) { + return result; + } else { + return cmp(size1, size2); + } + } + + case AVRO_ARRAY: + { + size_t count1; + size_t count2; + size_t min_count; + size_t i; + check_return(0, avro_value_get_size(val1, &count1)); + check_return(0, avro_value_get_size(val2, &count2)); + + min_count = (count1 < count2)? count1: count2; + for (i = 0; i < min_count; i++) { + avro_value_t child1; + avro_value_t child2; + int result; + check_return(0, avro_value_get_by_index + (val1, i, &child1, NULL)); + check_return(0, avro_value_get_by_index + (val2, i, &child2, NULL)); + result = avro_value_cmp_fast(&child1, &child2); + if (result != 0) { + return result; + } + } + + return cmp(count1, count2); + } + + case AVRO_ENUM: + { + int v1; + int v2; + check_return(0, avro_value_get_enum(val1, &v1)); + check_return(0, avro_value_get_enum(val2, &v2)); + return cmp(v1, v2); + } + + case AVRO_FIXED: + { + const void *buf1; + const void *buf2; + size_t size1; + size_t size2; + check_return(0, avro_value_get_fixed(val1, &buf1, &size1)); + check_return(0, avro_value_get_fixed(val2, &buf2, &size2)); + if (size1 != size2) { + return -1; + } + return memcmp(buf1, buf2, size1); + } + + case AVRO_MAP: + { + return -1; + } + + case AVRO_RECORD: + { + size_t count1; + check_return(0, avro_value_get_size(val1, &count1)); + + size_t i; + for (i = 0; i < count1; i++) { + avro_value_t child1; + avro_value_t child2; + int result; + + check_return(0, avro_value_get_by_index + (val1, i, &child1, NULL)); + check_return(0, avro_value_get_by_index + (val2, i, &child2, NULL)); + result = avro_value_cmp_fast(&child1, &child2); + if (result != 0) { + return result; + } + } + + return 0; + } + + case AVRO_UNION: + { + int disc1; + int disc2; + check_return(0, avro_value_get_discriminant(val1, &disc1)); + check_return(0, avro_value_get_discriminant(val2, &disc2)); + + if (disc1 == disc2) { + avro_value_t branch1; + avro_value_t branch2; + check_return(0, avro_value_get_current_branch(val1, &branch1)); + check_return(0, avro_value_get_current_branch(val2, &branch2)); + return avro_value_cmp_fast(&branch1, &branch2); + } else { + return cmp(disc1, disc2); + } + } + + default: + return 0; + } +} + +int +avro_value_cmp(avro_value_t *val1, avro_value_t *val2) +{ + avro_schema_t schema1 = avro_value_get_schema(val1); + avro_schema_t schema2 = avro_value_get_schema(val2); + if (!avro_schema_equal(schema1, schema2)) { + return 0; + } + + return avro_value_cmp_fast(val1, val2); +} + + +int +avro_value_copy_fast(avro_value_t *dest, const avro_value_t *src) +{ + avro_type_t dest_type = avro_value_get_type(dest); + avro_type_t src_type = avro_value_get_type(src); + if (dest_type != src_type) { + return 0; + } + + int rval; + check(rval, avro_value_reset(dest)); + + switch (dest_type) { + case AVRO_BOOLEAN: + { + int val; + check(rval, avro_value_get_boolean(src, &val)); + return avro_value_set_boolean(dest, val); + } + + case AVRO_BYTES: + { + avro_wrapped_buffer_t val; + check(rval, avro_value_grab_bytes(src, &val)); + return avro_value_give_bytes(dest, &val); + } + + case AVRO_DOUBLE: + { + double val; + check(rval, avro_value_get_double(src, &val)); + return avro_value_set_double(dest, val); + } + + case AVRO_FLOAT: + { + float val; + check(rval, avro_value_get_float(src, &val)); + return avro_value_set_float(dest, val); + } + + case AVRO_INT32: + { + int32_t val; + check(rval, avro_value_get_int(src, &val)); + return avro_value_set_int(dest, val); + } + + case AVRO_INT64: + { + int64_t val; + check(rval, avro_value_get_long(src, &val)); + return avro_value_set_long(dest, val); + } + + case AVRO_NULL: + { + check(rval, avro_value_get_null(src)); + return avro_value_set_null(dest); + } + + case AVRO_STRING: + { + avro_wrapped_buffer_t val; + check(rval, avro_value_grab_string(src, &val)); + return avro_value_give_string_len(dest, &val); + } + + case AVRO_ARRAY: + { + size_t count; + check(rval, avro_value_get_size(src, &count)); + + size_t i; + for (i = 0; i < count; i++) { + avro_value_t src_child; + avro_value_t dest_child; + + check(rval, avro_value_get_by_index + (src, i, &src_child, NULL)); + check(rval, avro_value_append + (dest, &dest_child, NULL)); + check(rval, avro_value_copy_fast + (&dest_child, &src_child)); + } + + return 0; + } + + case AVRO_ENUM: + { + int val; + check(rval, avro_value_get_enum(src, &val)); + return avro_value_set_enum(dest, val); + } + + case AVRO_FIXED: + { + avro_wrapped_buffer_t val; + check(rval, avro_value_grab_fixed(src, &val)); + return avro_value_give_fixed(dest, &val); + } + + case AVRO_MAP: + { + size_t count; + check(rval, avro_value_get_size(src, &count)); + + size_t i; + for (i = 0; i < count; i++) { + avro_value_t src_child; + avro_value_t dest_child; + const char *key; + + check(rval, avro_value_get_by_index + (src, i, &src_child, &key)); + check(rval, avro_value_add + (dest, key, &dest_child, NULL, NULL)); + check(rval, avro_value_copy_fast + (&dest_child, &src_child)); + } + + return 0; + } + + case AVRO_RECORD: + { + size_t count; + check(rval, avro_value_get_size(src, &count)); + + size_t i; + for (i = 0; i < count; i++) { + avro_value_t src_child; + avro_value_t dest_child; + + check(rval, avro_value_get_by_index + (src, i, &src_child, NULL)); + check(rval, avro_value_get_by_index + (dest, i, &dest_child, NULL)); + check(rval, avro_value_copy_fast + (&dest_child, &src_child)); + } + + return 0; + } + + case AVRO_UNION: + { + int disc; + check(rval, avro_value_get_discriminant(src, &disc)); + + avro_value_t src_branch; + avro_value_t dest_branch; + + check(rval, avro_value_get_current_branch(src, &src_branch)); + check(rval, avro_value_set_branch(dest, disc, &dest_branch)); + + return avro_value_copy_fast(&dest_branch, &src_branch); + } + + default: + return 0; + } +} + + +int +avro_value_copy(avro_value_t *dest, const avro_value_t *src) +{ + avro_schema_t dest_schema = avro_value_get_schema(dest); + avro_schema_t src_schema = avro_value_get_schema(src); + if (!avro_schema_equal(dest_schema, src_schema)) { + avro_set_error("Schemas don't match"); + return EINVAL; + } + + return avro_value_copy_fast(dest, src); +} diff --git a/fluent-bit/lib/avro/src/wrapped-buffer.c b/fluent-bit/lib/avro/src/wrapped-buffer.c new file mode 100644 index 000000000..e7496b424 --- /dev/null +++ b/fluent-bit/lib/avro/src/wrapped-buffer.c @@ -0,0 +1,145 @@ +/* + * 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 <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "avro_private.h" +#include "avro/allocation.h" +#include "avro/data.h" +#include "avro/refcount.h" + +struct avro_wrapped_copy { + volatile int refcount; + size_t allocated_size; +}; + +static void +avro_wrapped_copy_free(avro_wrapped_buffer_t *self) +{ + struct avro_wrapped_copy *copy = (struct avro_wrapped_copy *) self->user_data; + if (avro_refcount_dec(©->refcount)) { + avro_free(copy, copy->allocated_size); + } +} + +static int +avro_wrapped_copy_copy(avro_wrapped_buffer_t *dest, + const avro_wrapped_buffer_t *src, + size_t offset, size_t length) +{ + struct avro_wrapped_copy *copy = (struct avro_wrapped_copy *) src->user_data; + avro_refcount_inc(©->refcount); + dest->buf = (char *) src->buf + offset; + dest->size = length; + dest->user_data = copy; + dest->free = avro_wrapped_copy_free; + dest->copy = avro_wrapped_copy_copy; + dest->slice = NULL; + return 0; +} + +int +avro_wrapped_buffer_new_copy(avro_wrapped_buffer_t *dest, + const void *buf, size_t length) +{ + size_t allocated_size = sizeof(struct avro_wrapped_copy) + length; + struct avro_wrapped_copy *copy = (struct avro_wrapped_copy *) avro_malloc(allocated_size); + if (copy == NULL) { + return ENOMEM; + } + + dest->buf = ((char *) copy) + sizeof(struct avro_wrapped_copy); + dest->size = length; + dest->user_data = copy; + dest->free = avro_wrapped_copy_free; + dest->copy = avro_wrapped_copy_copy; + dest->slice = NULL; + + avro_refcount_set(©->refcount, 1); + copy->allocated_size = allocated_size; + memcpy((void *) dest->buf, buf, length); + return 0; +} + +int +avro_wrapped_buffer_new(avro_wrapped_buffer_t *dest, + const void *buf, size_t length) +{ + dest->buf = buf; + dest->size = length; + dest->user_data = NULL; + dest->free = NULL; + dest->copy = NULL; + dest->slice = NULL; + return 0; +} + + +void +avro_wrapped_buffer_move(avro_wrapped_buffer_t *dest, + avro_wrapped_buffer_t *src) +{ + memcpy(dest, src, sizeof(avro_wrapped_buffer_t)); + memset(src, 0, sizeof(avro_wrapped_buffer_t)); +} + +int +avro_wrapped_buffer_copy(avro_wrapped_buffer_t *dest, + const avro_wrapped_buffer_t *src, + size_t offset, size_t length) +{ + if (offset > src->size) { + avro_set_error("Invalid offset when slicing buffer"); + return EINVAL; + } + + if ((offset+length) > src->size) { + avro_set_error("Invalid length when slicing buffer"); + return EINVAL; + } + + if (src->copy == NULL) { + return avro_wrapped_buffer_new_copy(dest, (char *) src->buf + offset, length); + } else { + return src->copy(dest, src, offset, length); + } +} + +int +avro_wrapped_buffer_slice(avro_wrapped_buffer_t *self, + size_t offset, size_t length) +{ + if (offset > self->size) { + avro_set_error("Invalid offset when slicing buffer"); + return EINVAL; + } + + if ((offset+length) > self->size) { + avro_set_error("Invalid length when slicing buffer"); + return EINVAL; + } + + if (self->slice == NULL) { + self->buf = (char *) self->buf + offset; + self->size = length; + return 0; + } else { + return self->slice(self, offset, length); + } +} |