From 3e5744bbe242ed13fb3ed2bbe77c634bf20385a9 Mon Sep 17 00:00:00 2001 From: flpereir Date: Wed, 15 Apr 2026 15:28:07 +0200 Subject: [PATCH 1/3] AVRO-4246: Add 'AVRO_READ_OR_FREE' macro. --- lang/c/src/encoding.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lang/c/src/encoding.h b/lang/c/src/encoding.h index 6333d588df7..b90c91dd5c8 100644 --- a/lang/c/src/encoding.h +++ b/lang/c/src/encoding.h @@ -96,6 +96,8 @@ typedef struct avro_encoding_t avro_encoding_t; { 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_READ_OR_FREE(reader, buf, len) \ +{ int rval = avro_read( reader, buf, len ); if(rval) { avro_free(buf, 0); buf = NULL; return rval; } } #define AVRO_SKIP(reader, len) \ { int rval = avro_skip( reader, len); if (rval) return rval; } From 0fdcd1d7b91842d3f2909c55882c5809b16520dd Mon Sep 17 00:00:00 2001 From: flpereir Date: Wed, 15 Apr 2026 15:28:19 +0200 Subject: [PATCH 2/3] AVRO-4246: Replace 'AVRO_READ' by 'AVRO_READ_OR_FREE' in 'read_string' and 'read_bytes' functions. --- lang/c/src/encoding_binary.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/c/src/encoding_binary.c b/lang/c/src/encoding_binary.c index 96dacea5836..459d5fb3db3 100644 --- a/lang/c/src/encoding_binary.c +++ b/lang/c/src/encoding_binary.c @@ -136,8 +136,8 @@ static int read_bytes(avro_reader_t reader, char **bytes, int64_t * len) avro_set_error("Cannot allocate buffer for bytes value"); return ENOMEM; } - AVRO_READ(reader, *bytes, *len); (*bytes)[*len] = '\0'; + AVRO_READ_OR_FREE(reader, *bytes, *len); return 0; } @@ -194,7 +194,7 @@ static int read_string(avro_reader_t reader, char **s, int64_t *len) return ENOMEM; } (*s)[str_len] = '\0'; - AVRO_READ(reader, *s, str_len); + AVRO_READ_OR_FREE(reader, *s, str_len); return 0; } From a874ecdf4d709e930f05f5f971ea22657b3320f3 Mon Sep 17 00:00:00 2001 From: flpereir Date: Wed, 15 Apr 2026 17:07:31 +0200 Subject: [PATCH 3/3] AVRO-4246: Add 'test_avro_4246.c' to test suite. --- lang/c/tests/CMakeLists.txt | 1 + lang/c/tests/test_avro_4246.c | 223 ++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 lang/c/tests/test_avro_4246.c diff --git a/lang/c/tests/CMakeLists.txt b/lang/c/tests/CMakeLists.txt index 3200164770d..6b4164fa740 100644 --- a/lang/c/tests/CMakeLists.txt +++ b/lang/c/tests/CMakeLists.txt @@ -87,3 +87,4 @@ add_avro_test_checkmem(test_avro_1379) add_avro_test_checkmem(test_avro_1691) add_avro_test_checkmem(test_avro_1906) add_avro_test_checkmem(test_avro_1904) +add_avro_test_checkmem(test_avro_4246) diff --git a/lang/c/tests/test_avro_4246.c b/lang/c/tests/test_avro_4246.c new file mode 100644 index 00000000000..d29b2c3bbc0 --- /dev/null +++ b/lang/c/tests/test_avro_4246.c @@ -0,0 +1,223 @@ +/* + * 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 +#include +#include +#include +#include + +#define STRING_SCHEMA_LITERAL "\"string\"" +#define INT_SCHEMA_LITERAL "\"int\"" + +typedef struct +{ + char data[256]; + size_t size; +} encoded_buf_t; + +static int encode_int( + avro_value_iface_t *iface, + int32_t number, + encoded_buf_t *out_buf) +{ + int rc; + avro_value_t value; + avro_writer_t writer = NULL; + + memset(out_buf, 0, sizeof(*out_buf)); + + rc = avro_generic_value_new(iface, &value); + if (rc != 0) + { + fprintf(stderr, "encode_int: avro_generic_value_new failed: %s\n", + avro_strerror()); + return rc; + } + + rc = avro_value_set_int(&value, number); + if (rc != 0) + { + fprintf(stderr, "encode_int: avro_value_set_int failed: %s\n", + avro_strerror()); + avro_value_decref(&value); + return rc; + } + + writer = avro_writer_memory(out_buf->data, sizeof(out_buf->data)); + if (writer == NULL) + { + fprintf(stderr, "encode_int: avro_writer_memory failed\n"); + avro_value_decref(&value); + return -1; + } + + rc = avro_value_write(writer, &value); + if (rc != 0) + { + fprintf(stderr, "encode_int: avro_value_write failed: %s\n", + avro_strerror()); + avro_writer_free(writer); + avro_value_decref(&value); + return rc; + } + + out_buf->size = avro_writer_tell(writer); + + avro_writer_free(writer); + avro_value_decref(&value); + return 0; +} + +static int decode_as_string( + avro_value_iface_t *string_iface, + const void *buf, + size_t buf_size, + const char *label) +{ + int rc; + avro_reader_t reader = NULL; + avro_value_t decoded; + const char *text = NULL; + size_t text_size = 0; + + rc = avro_generic_value_new(string_iface, &decoded); + if (rc != 0) + { + fprintf(stderr, "%s: avro_generic_value_new failed: %s\n", + label, avro_strerror()); + return rc; + } + + reader = avro_reader_memory((const char *)buf, buf_size); + if (reader == NULL) + { + fprintf(stderr, "%s: avro_reader_memory failed\n", label); + avro_value_decref(&decoded); + return -1; + } + + rc = avro_value_read(reader, &decoded); + if (rc != 0) + { + fprintf(stderr, "%s: avro_value_read failed as expected? rc=%d err=%s\n", + label, rc, avro_strerror()); + avro_reader_free(reader); + avro_value_decref(&decoded); + return rc; + } + + rc = avro_value_get_string(&decoded, &text, &text_size); + if (rc != 0) + { + fprintf(stderr, "%s: avro_value_get_string failed: %s\n", + label, avro_strerror()); + avro_reader_free(reader); + avro_value_decref(&decoded); + return rc; + } + + printf("%s: decoded successfully: \"%s\" (%zu bytes)\n", + label, text, text_size); + + avro_reader_free(reader); + avro_value_decref(&decoded); + return 0; +} + +int main(void) +{ + int rc; + int ret = EXIT_SUCCESS; + + avro_schema_t string_schema = NULL; + avro_schema_t int_schema = NULL; + + avro_value_iface_t *string_iface = NULL; + avro_value_iface_t *int_iface = NULL; + + encoded_buf_t encoded_string; + encoded_buf_t encoded_int; + + rc = avro_schema_from_json_literal(STRING_SCHEMA_LITERAL, &string_schema); + if (rc != 0) + { + fprintf(stderr, "Failed to parse string schema: %s\n", avro_strerror()); + return EXIT_FAILURE; + } + + rc = avro_schema_from_json_literal(INT_SCHEMA_LITERAL, &int_schema); + if (rc != 0) + { + fprintf(stderr, "Failed to parse int schema: %s\n", avro_strerror()); + avro_schema_decref(string_schema); + return EXIT_FAILURE; + } + + string_iface = avro_generic_class_from_schema(string_schema); + if (string_iface == NULL) + { + fprintf(stderr, "Failed to create string iface\n"); + avro_schema_decref(int_schema); + avro_schema_decref(string_schema); + return EXIT_FAILURE; + } + + int_iface = avro_generic_class_from_schema(int_schema); + if (int_iface == NULL) + { + fprintf(stderr, "Failed to create int iface\n"); + avro_value_iface_decref(string_iface); + avro_schema_decref(int_schema); + avro_schema_decref(string_schema); + return EXIT_FAILURE; + } + + rc = encode_int(int_iface, 12345, &encoded_int); + if (rc != 0) + { + fprintf(stderr, "Encoding int failed\n"); + avro_value_iface_decref(int_iface); + avro_value_iface_decref(string_iface); + avro_schema_decref(int_schema); + avro_schema_decref(string_schema); + return EXIT_FAILURE; + } + + printf("Encoded int size: %zu bytes\n", encoded_int.size); + + rc = decode_as_string(string_iface, + encoded_int.data, + encoded_int.size, + "int payload as string"); + if (rc == 0) + { + fprintf(stderr, "Unexpected success decoding int payload as string\n"); + ret = EXIT_FAILURE; + } + else + { + printf("int payload as string: failed cleanly, as expected\n"); + } + + avro_value_iface_decref(int_iface); + avro_value_iface_decref(string_iface); + avro_schema_decref(int_schema); + avro_schema_decref(string_schema); + + return ret; +}