37 if (strcmp(type_str,
"null") == 0)
return AVRO_NULL;
38 if (strcmp(type_str,
"boolean") == 0)
return AVRO_BOOLEAN;
39 if (strcmp(type_str,
"int") == 0)
return AVRO_INT;
40 if (strcmp(type_str,
"long") == 0)
return AVRO_LONG;
41 if (strcmp(type_str,
"float") == 0)
return AVRO_FLOAT;
42 if (strcmp(type_str,
"double") == 0)
return AVRO_DOUBLE;
43 if (strcmp(type_str,
"bytes") == 0)
return AVRO_BYTES;
44 if (strcmp(type_str,
"string") == 0)
return AVRO_STRING;
80 if (cJSON_IsString(json)) {
86 if (cJSON_IsArray(json)) {
88 int nb = cJSON_GetArraySize(json);
92 for (
int i = 0; i < nb; i++) {
99 if (cJSON_IsObject(json)) {
100 const cJSON* type_node = cJSON_GetObjectItemCaseSensitive(json,
"type");
103 const char* type_str = cJSON_GetStringValue(type_node);
114 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json,
"name");
115 if (name_node && cJSON_IsString(name_node)) {
121 if (strcmp(type_str,
"record") == 0) {
124 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json,
"name");
125 if (name_node && cJSON_IsString(name_node)) {
129 const cJSON* ns_node = cJSON_GetObjectItemCaseSensitive(json,
"namespace");
130 if (ns_node && cJSON_IsString(ns_node)) {
131 schema->namespace =
local_strdup(cJSON_GetStringValue(ns_node));
134 const cJSON* fields_node = cJSON_GetObjectItemCaseSensitive(json,
"fields");
135 if (fields_node && cJSON_IsArray(fields_node)) {
136 int nb = cJSON_GetArraySize(fields_node);
141 for (
int i = 0; i < nb; i++) {
142 const cJSON* field = cJSON_GetArrayItem(fields_node, i);
143 const cJSON* fname = cJSON_GetObjectItemCaseSensitive(field,
"name");
144 const cJSON* ftype = cJSON_GetObjectItemCaseSensitive(field,
"type");
146 if (fname && cJSON_IsString(fname)) {
152 }
else if (strcmp(type_str,
"array") == 0) {
154 const cJSON* items_node = cJSON_GetObjectItemCaseSensitive(json,
"items");
156 }
else if (strcmp(type_str,
"map") == 0) {
158 const cJSON* values_node = cJSON_GetObjectItemCaseSensitive(json,
"values");
160 }
else if (strcmp(type_str,
"enum") == 0) {
163 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json,
"name");
164 if (name_node && cJSON_IsString(name_node)) {
168 const cJSON* ns_node = cJSON_GetObjectItemCaseSensitive(json,
"namespace");
169 if (ns_node && cJSON_IsString(ns_node)) {
170 schema->namespace =
local_strdup(cJSON_GetStringValue(ns_node));
173 const cJSON* symbols_node = cJSON_GetObjectItemCaseSensitive(json,
"symbols");
174 if (symbols_node && cJSON_IsArray(symbols_node)) {
175 int nb = cJSON_GetArraySize(symbols_node);
179 for (
int i = 0; i < nb; i++) {
180 const cJSON* sym = cJSON_GetArrayItem(symbols_node, i);
181 if (cJSON_IsString(sym)) {
186 }
else if (strcmp(type_str,
"fixed") == 0) {
189 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json,
"name");
190 if (name_node && cJSON_IsString(name_node)) {
194 const cJSON* size_node = cJSON_GetObjectItemCaseSensitive(json,
"size");
195 if (size_node && cJSON_IsNumber(size_node)) {
196 schema->
fixed_size = (size_t)cJSON_GetNumberValue(size_node);
213 cJSON* json = cJSON_Parse(json_str);
222 if (!schema_ptr || !*schema_ptr)
return;
230 for (
size_t i = 0; i < schema->
nb_fields; i++) {
248 for (
size_t i = 0; i < schema->
nb_symbols; i++) {
262 switch (schema->
type) {
276 json = cJSON_CreateObject();
277 cJSON_AddStringToObject(json,
"type",
"record");
279 cJSON_AddStringToObject(json,
"name", schema->
name);
281 if (schema->namespace) {
282 cJSON_AddStringToObject(json,
"namespace", schema->namespace);
284 cJSON* fields_arr = cJSON_AddArrayToObject(json,
"fields");
285 for (
size_t i = 0; i < schema->
nb_fields; i++) {
286 cJSON* field = cJSON_CreateObject();
287 cJSON_AddStringToObject(field,
"name", schema->
fields[i].
name);
290 cJSON* ftype = cJSON_Parse(ftype_str);
291 cJSON_AddItemToObject(field,
"type", ftype);
294 cJSON_AddItemToArray(fields_arr, field);
300 json = cJSON_CreateObject();
301 cJSON_AddStringToObject(json,
"type",
"array");
304 cJSON* items = cJSON_Parse(items_str);
305 cJSON_AddItemToObject(json,
"items", items);
312 json = cJSON_CreateObject();
313 cJSON_AddStringToObject(json,
"type",
"map");
316 cJSON* values = cJSON_Parse(values_str);
317 cJSON_AddItemToObject(json,
"values", values);
324 json = cJSON_CreateObject();
325 cJSON_AddStringToObject(json,
"type",
"enum");
327 cJSON_AddStringToObject(json,
"name", schema->
name);
329 if (schema->namespace) {
330 cJSON_AddStringToObject(json,
"namespace", schema->namespace);
332 cJSON* syms = cJSON_AddArrayToObject(json,
"symbols");
333 for (
size_t i = 0; i < schema->
nb_symbols; i++) {
334 cJSON_AddItemToArray(syms, cJSON_CreateString(schema->
symbols[i]));
340 json = cJSON_CreateArray();
344 cJSON* branch = cJSON_Parse(branch_str);
345 cJSON_AddItemToArray(json, branch);
353 json = cJSON_CreateObject();
354 cJSON_AddStringToObject(json,
"type",
"fixed");
356 cJSON_AddStringToObject(json,
"name", schema->
name);
358 cJSON_AddNumberToObject(json,
"size", (
double)schema->
fixed_size);
365 result = cJSON_PrintUnformatted(json);
377 uint64_t n = ((uint64_t)value << 1) ^ -(uint64_t)(value < 0);
379 unsigned char buf[10];
382 buf[len++] = (
unsigned char)((n & 0x7F) | 0x80);
385 buf[len++] = (
unsigned char)n;
398 while (reader->
pos < reader->
size) {
399 unsigned char b = reader->
data[reader->
pos++];
400 n |= ((uint64_t)(b & 0x7F)) << shift;
401 if ((b & 0x80) == 0) {
403 *value = (int64_t)((n >> 1) ^ (~(n & 1) + 1));
413 n_log(
LOG_ERR,
"unexpected end of data while reading varint");
422 if (ret != TRUE)
return ret;
433 switch (branch->
type) {
435 if (cJSON_IsNull(json))
return (
int)i;
438 if (cJSON_IsBool(json))
return (
int)i;
444 if (cJSON_IsNumber(json))
return (
int)i;
447 if (cJSON_IsString(json))
return (
int)i;
450 if (cJSON_IsString(json))
return (
int)i;
454 if (cJSON_IsObject(json))
return (
int)i;
457 if (cJSON_IsArray(json))
return (
int)i;
460 if (cJSON_IsString(json))
return (
int)i;
474 switch (schema->
type) {
480 unsigned char b = cJSON_IsTrue(json) ? 1 : 0;
487 if (cJSON_IsNumber(json)) {
488 val = (int32_t)cJSON_GetNumberValue(json);
495 if (cJSON_IsNumber(json)) {
496 val = (int64_t)cJSON_GetNumberValue(json);
503 if (cJSON_IsNumber(json)) {
504 val = (float)cJSON_GetNumberValue(json);
507 unsigned char buf[4];
508 memcpy(buf, &val, 4);
509#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
524 if (cJSON_IsNumber(json)) {
525 val = cJSON_GetNumberValue(json);
528 unsigned char buf[8];
529 memcpy(buf, &val, 8);
530#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
550 const char* str = cJSON_GetStringValue((cJSON*)json);
558 const char* str = cJSON_GetStringValue((cJSON*)json);
566 if (!cJSON_IsObject(json)) {
570 for (
size_t i = 0; i < schema->
nb_fields; i++) {
571 const cJSON* field_val = cJSON_GetObjectItemCaseSensitive(json, schema->
fields[i].
name);
575 memset(&null_val, 0,
sizeof(null_val));
576 null_val.type = cJSON_NULL;
590 const char* sym = cJSON_GetStringValue((cJSON*)json);
595 for (
size_t i = 0; i < schema->
nb_symbols; i++) {
596 if (strcmp(sym, schema->
symbols[i]) == 0) {
605 if (!cJSON_IsArray(json)) {
609 int count = cJSON_GetArraySize(json);
613 for (
int i = 0; i < count; i++) {
624 if (!cJSON_IsObject(json)) {
628 int count = cJSON_GetArraySize(json);
631 const cJSON* item = NULL;
632 cJSON_ArrayForEach(item, json) {
634 const char*
key = item->string;
649 if (branch_idx < 0) {
650 n_log(
LOG_ERR,
"no matching union branch for JSON value");
658 const char* str = cJSON_GetStringValue((cJSON*)json);
663 size_t len = strlen(str);
669 if (len < schema->fixed_size) {
671 unsigned char* zeros = NULL;
672 Malloc(zeros,
unsigned char, pad);
695 *out_len = (size_t)len;
696 if ((
size_t)len > reader->
size - reader->
pos) {
697 n_log(
LOG_ERR,
"not enough data for bytes: need %zu, have %zu", (
size_t)len, reader->
size - reader->
pos);
700 Malloc(*out,
unsigned char, (
size_t)len + 1);
703 memcpy(*out, reader->
data + reader->
pos, (
size_t)len);
705 (*out)[(size_t)len] =
'\0';
706 reader->
pos += (size_t)len;
714 switch (schema->
type) {
716 return cJSON_CreateNull();
719 if (reader->
pos >= reader->
size) {
720 n_log(
LOG_ERR,
"unexpected end of data reading boolean");
723 unsigned char b = reader->
data[reader->
pos++];
724 return cJSON_CreateBool(b != 0);
730 return cJSON_CreateNumber((
double)(int32_t)val);
736 return cJSON_CreateNumber((
double)val);
740 if (reader->
pos + 4 > reader->
size) {
744 unsigned char buf[4];
745 memcpy(buf, reader->
data + reader->
pos, 4);
747#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
757 memcpy(&val, buf, 4);
758 return cJSON_CreateNumber((
double)val);
762 if (reader->
pos + 8 > reader->
size) {
763 n_log(
LOG_ERR,
"unexpected end of data reading double");
766 unsigned char buf[8];
767 memcpy(buf, reader->
data + reader->
pos, 8);
769#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
785 memcpy(&val, buf, 8);
786 return cJSON_CreateNumber(val);
790 unsigned char* data = NULL;
793 cJSON* result = cJSON_CreateString((
const char*)data);
799 unsigned char* data = NULL;
802 cJSON* result = cJSON_CreateString((
const char*)data);
808 cJSON* obj = cJSON_CreateObject();
809 for (
size_t i = 0; i < schema->
nb_fields; i++) {
815 cJSON_AddItemToObject(obj, schema->
fields[i].
name, field_val);
823 if (idx < 0 || (
size_t)idx >= schema->
nb_symbols) {
827 return cJSON_CreateString(schema->
symbols[(
size_t)idx]);
831 cJSON* arr = cJSON_CreateArray();
832 int64_t block_count = 0;
838 if (block_count == 0)
break;
839 if (block_count < 0) {
841 block_count = -block_count;
842 int64_t block_size = 0;
849 for (int64_t i = 0; i < block_count; i++) {
855 cJSON_AddItemToArray(arr, item);
862 cJSON* obj = cJSON_CreateObject();
863 int64_t block_count = 0;
869 if (block_count == 0)
break;
870 if (block_count < 0) {
871 block_count = -block_count;
872 int64_t block_size = 0;
879 for (int64_t i = 0; i < block_count; i++) {
881 unsigned char*
key = NULL;
894 cJSON_AddItemToObject(obj, (
const char*)
key, val);
902 int64_t branch_idx = 0;
904 if (branch_idx < 0 || (
size_t)branch_idx >= schema->
nb_branches) {
922 cJSON* result = cJSON_CreateString(buf);
937 if (!cJSON_IsArray(records)) {
942 N_STR* output = NULL;
958 const char* key1 =
"avro.schema";
965 const char* key2 =
"avro.codec";
968 const char* codec =
"null";
979 srand((
unsigned)time(NULL));
981 sync[i] = (
unsigned char)(rand() % 256);
986 int count = cJSON_GetArraySize(records);
988 N_STR* block_data = NULL;
992 for (
int i = 0; i < count; i++) {
993 const cJSON* record = cJSON_GetArrayItem(records, i);
1022 reader.
data = (
const unsigned char*)avro_data->
data;
1038 int64_t block_count = 0;
1041 if (block_count == 0)
break;
1042 if (block_count < 0) {
1044 int64_t block_size = 0;
1046 if (block_size < 0 || (
size_t)block_size > reader.
size - reader.
pos) {
1050 reader.
pos += (size_t)block_size;
1054 for (int64_t i = 0; i < block_count; i++) {
1055 unsigned char*
key = NULL;
1059 unsigned char* val = NULL;
1068 n_log(
LOG_ERR,
"unexpected end of data reading sync marker");
1076 cJSON* all_records = cJSON_CreateArray();
1078 while (reader.
pos < reader.
size) {
1079 int64_t obj_count = 0;
1081 cJSON_Delete(all_records);
1084 if (obj_count <= 0)
break;
1086 int64_t block_byte_size = 0;
1088 cJSON_Delete(all_records);
1092 if (block_byte_size < 0 || (
size_t)block_byte_size > reader.
size - reader.
pos) {
1093 n_log(
LOG_ERR,
"invalid data block size: %" PRId64, block_byte_size);
1094 cJSON_Delete(all_records);
1098 size_t block_end = reader.
pos + (size_t)block_byte_size;
1100 for (int64_t i = 0; i < obj_count; i++) {
1104 cJSON_Delete(all_records);
1107 cJSON_AddItemToArray(all_records, record);
1111 if (reader.
pos != block_end) {
1112 n_log(
LOG_WARNING,
"block size mismatch: expected pos %zu, got %zu", block_end, reader.
pos);
1113 reader.
pos = block_end;
1118 n_log(
LOG_ERR,
"unexpected end of data reading block sync marker");
1119 cJSON_Delete(all_records);
1124 cJSON_Delete(all_records);
1135int avro_json_to_file(
const char* avro_filename,
const char* schema_filename,
const char* json_filename) {
1155 cJSON* json = cJSON_Parse(json_str->
data);
1158 n_log(
LOG_ERR,
"failed to parse JSON file %s: %s", json_filename,
_str(cJSON_GetErrorPtr()));
1164 cJSON* records = json;
1166 if (cJSON_IsObject(json)) {
1167 records = cJSON_CreateArray();
1168 cJSON_AddItemToArray(records, cJSON_Duplicate(json, 1));
1176 cJSON_Delete(records);
1187 int ret =
nstr_to_file(avro_data, (
char*)avro_filename);
1191 n_log(
LOG_ERR,
"failed to write Avro file %s", avro_filename);
1199int avro_file_to_json(
const char* avro_filename,
const char* schema_filename,
const char* json_filename) {
1225 n_log(
LOG_ERR,
"failed to decode Avro file %s", avro_filename);
1230 char* json_output = cJSON_Print(records);
1231 cJSON_Delete(records);
1245 int ret =
nstr_to_file(json_nstr, (
char*)json_filename);
1249 n_log(
LOG_ERR,
"failed to write JSON file %s", json_filename);
1275 cJSON* json = cJSON_Parse(json_nstr->
data);
1283 cJSON* records = json;
1285 if (cJSON_IsObject(json)) {
1286 records = cJSON_CreateArray();
1287 cJSON_AddItemToArray(records, cJSON_Duplicate(json, 1));
1294 cJSON_Delete(records);
1319 char* json_output = cJSON_Print(records);
1320 cJSON_Delete(records);
size_t nb_symbols
number of enum symbols
AVRO_SCHEMA ** union_branches
union branch schemas
char * name
name (for record, enum, fixed)
AVRO_SCHEMA * items
item schema (for array)
AVRO_SCHEMA * values
value schema (for map)
AVRO_FIELD * fields
namespace (for record, enum)
size_t pos
current position
size_t fixed_size
fixed size
AVRO_SCHEMA * schema
field schema
AVRO_TYPE type
schema type
const unsigned char * data
data buffer
size_t nb_branches
number of union branches
char ** symbols
enum symbols
size_t nb_fields
number of fields (for record)
N_STR * avro_nstr_json_to_avro(const N_STR *schema_nstr, const N_STR *json_nstr)
Convert JSON N_STR to Avro N_STR using schema N_STR (all in-memory)
#define AVRO_SYNC_LEN
Avro sync marker length.
AVRO_TYPE
Avro schema type enumeration.
#define AVRO_MAGIC_LEN
Avro magic length.
int avro_file_to_json(const char *avro_filename, const char *schema_filename, const char *json_filename)
Read an Avro object container file and produce a JSON file using a schema file.
N_STR * avro_encode_container(const AVRO_SCHEMA *schema, const cJSON *records)
Encode a cJSON array of records into Avro container format N_STR.
AVRO_SCHEMA * avro_schema_from_cjson(const cJSON *json)
Parse an Avro schema from a cJSON object.
int avro_encode_datum(N_STR **dest, const AVRO_SCHEMA *schema, const cJSON *json)
Encode a cJSON value as Avro binary according to schema.
AVRO_SCHEMA * avro_schema_parse_nstr(const N_STR *schema_nstr)
Parse schema from N_STR.
AVRO_SCHEMA * avro_schema_parse(const char *json_str)
Parse an Avro schema from a JSON string.
int avro_decode_long(AVRO_READER *reader, int64_t *value)
Decode a zig-zag varint from reader into a 64-bit signed integer.
cJSON * avro_decode_container(const AVRO_SCHEMA *schema, const N_STR *avro_data)
Decode an Avro container format N_STR into a cJSON array of records.
cJSON * avro_decode_datum(AVRO_READER *reader, const AVRO_SCHEMA *schema)
Decode an Avro binary datum into cJSON according to schema.
N_STR * avro_nstr_avro_to_json(const N_STR *schema_nstr, const N_STR *avro_nstr)
Convert Avro N_STR to JSON N_STR using schema N_STR (all in-memory)
char * avro_schema_to_json(const AVRO_SCHEMA *schema)
Convert an Avro schema back to JSON string (caller must free)
void avro_schema_free(AVRO_SCHEMA **schema_ptr)
Free an Avro schema.
int avro_encode_long(N_STR **dest, int64_t value)
Encode a 64-bit signed integer as zig-zag varint into N_STR.
#define AVRO_MAGIC
Avro object container file magic bytes.
int avro_json_to_file(const char *avro_filename, const char *schema_filename, const char *json_filename)
Write an Avro object container file from a JSON file and schema file.
Avro schema field (for records)
Avro read cursor for decoding.
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define _str(__PTR)
define true
#define Free(__ptr)
Free Handler to get errors.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
#define LOG_WARNING
warning conditions
#define LOG_INFO
informational
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
size_t NSTRBYTE
N_STR base unit.
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
#define local_strdup(__src_)
Do tar(1) matching rules, which ignore a trailing slash?
int nstr_to_file(N_STR *str, char *filename)
Write a N_STR content into a file.
N_STR * nstrcat_ex(N_STR **dest, void *src, NSTRBYTE size, int resize_flag)
Append data into N_STR using internal N_STR size and cursor position.
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
N_STR * file_to_nstr(char *filename)
Load a whole file into a N_STR.
A box including a string and his lenght.
static int avro_encode_bytes_raw(N_STR **dest, const unsigned char *data, size_t len)
helper: encode raw bytes with length prefix
static AVRO_TYPE avro_type_from_string(const char *type_str)
helper to get AVRO_TYPE from a type name string
static int avro_decode_bytes_raw(AVRO_READER *reader, unsigned char **out, size_t *out_len)
helper: read raw bytes with length prefix
static const char * avro_type_to_string(AVRO_TYPE type)
helper to get type name from AVRO_TYPE
static int avro_find_union_branch(const AVRO_SCHEMA *schema, const cJSON *json)
helper: find which union branch matches a cJSON value
Avro binary format encoding/decoding with JSON conversion.