Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_avro.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include "nilorea/n_avro.h"
29
30#include <string.h>
31#include <time.h>
32
33/* Schema parsing */
34
36static AVRO_TYPE avro_type_from_string(const char* type_str) {
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;
45 return AVRO_NULL;
46}
47
49static const char* avro_type_to_string(AVRO_TYPE type) {
50 switch (type) {
51 case AVRO_NULL:
52 return "null";
53 case AVRO_BOOLEAN:
54 return "boolean";
55 case AVRO_INT:
56 return "int";
57 case AVRO_LONG:
58 return "long";
59 case AVRO_FLOAT:
60 return "float";
61 case AVRO_DOUBLE:
62 return "double";
63 case AVRO_BYTES:
64 return "bytes";
65 case AVRO_STRING:
66 return "string";
67 default:
68 return "null";
69 }
70}
71
73 __n_assert(json, return NULL);
74
75 AVRO_SCHEMA* schema = NULL;
76 Malloc(schema, AVRO_SCHEMA, 1);
77 __n_assert(schema, return NULL);
78
79 /* primitive type as string: "null", "boolean", "int", etc. */
80 if (cJSON_IsString(json)) {
81 schema->type = avro_type_from_string(cJSON_GetStringValue((cJSON*)json));
82 return schema;
83 }
84
85 /* union: JSON array of schemas */
86 if (cJSON_IsArray(json)) {
87 schema->type = AVRO_UNION;
88 int nb = cJSON_GetArraySize(json);
89 schema->nb_branches = (size_t)nb;
90 Malloc(schema->union_branches, AVRO_SCHEMA*, (size_t)nb);
91 __n_assert(schema->union_branches, Free(schema); return NULL);
92 for (int i = 0; i < nb; i++) {
93 schema->union_branches[i] = avro_schema_from_cjson(cJSON_GetArrayItem(json, i));
94 }
95 return schema;
96 }
97
98 /* complex type as object */
99 if (cJSON_IsObject(json)) {
100 const cJSON* type_node = cJSON_GetObjectItemCaseSensitive(json, "type");
101 __n_assert(type_node, Free(schema); return NULL);
102
103 const char* type_str = cJSON_GetStringValue(type_node);
104
105 /* type field can itself be a complex type (array or object) */
106 if (!type_str) {
107 /* nested complex type in the "type" field */
108 AVRO_SCHEMA* inner = avro_schema_from_cjson(type_node);
109 if (inner) {
110 memcpy(schema, inner, sizeof(AVRO_SCHEMA));
111 Free(inner);
112 }
113 /* copy name/namespace from outer object if present */
114 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json, "name");
115 if (name_node && cJSON_IsString(name_node)) {
116 schema->name = local_strdup(cJSON_GetStringValue(name_node));
117 }
118 return schema;
119 }
120
121 if (strcmp(type_str, "record") == 0) {
122 schema->type = AVRO_RECORD;
123
124 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json, "name");
125 if (name_node && cJSON_IsString(name_node)) {
126 schema->name = local_strdup(cJSON_GetStringValue(name_node));
127 }
128
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));
132 }
133
134 const cJSON* fields_node = cJSON_GetObjectItemCaseSensitive(json, "fields");
135 if (fields_node && cJSON_IsArray(fields_node)) {
136 int nb = cJSON_GetArraySize(fields_node);
137 schema->nb_fields = (size_t)nb;
138 Malloc(schema->fields, AVRO_FIELD, (size_t)nb);
139 __n_assert(schema->fields, avro_schema_free(&schema); return NULL);
140
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");
145
146 if (fname && cJSON_IsString(fname)) {
147 schema->fields[i].name = local_strdup(cJSON_GetStringValue(fname));
148 }
149 schema->fields[i].schema = avro_schema_from_cjson(ftype);
150 }
151 }
152 } else if (strcmp(type_str, "array") == 0) {
153 schema->type = AVRO_ARRAY;
154 const cJSON* items_node = cJSON_GetObjectItemCaseSensitive(json, "items");
155 schema->items = avro_schema_from_cjson(items_node);
156 } else if (strcmp(type_str, "map") == 0) {
157 schema->type = AVRO_MAP;
158 const cJSON* values_node = cJSON_GetObjectItemCaseSensitive(json, "values");
159 schema->values = avro_schema_from_cjson(values_node);
160 } else if (strcmp(type_str, "enum") == 0) {
161 schema->type = AVRO_ENUM;
162
163 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json, "name");
164 if (name_node && cJSON_IsString(name_node)) {
165 schema->name = local_strdup(cJSON_GetStringValue(name_node));
166 }
167
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));
171 }
172
173 const cJSON* symbols_node = cJSON_GetObjectItemCaseSensitive(json, "symbols");
174 if (symbols_node && cJSON_IsArray(symbols_node)) {
175 int nb = cJSON_GetArraySize(symbols_node);
176 schema->nb_symbols = (size_t)nb;
177 Malloc(schema->symbols, char*, (size_t)nb);
178 __n_assert(schema->symbols, avro_schema_free(&schema); return NULL);
179 for (int i = 0; i < nb; i++) {
180 const cJSON* sym = cJSON_GetArrayItem(symbols_node, i);
181 if (cJSON_IsString(sym)) {
182 schema->symbols[i] = local_strdup(cJSON_GetStringValue(sym));
183 }
184 }
185 }
186 } else if (strcmp(type_str, "fixed") == 0) {
187 schema->type = AVRO_FIXED;
188
189 const cJSON* name_node = cJSON_GetObjectItemCaseSensitive(json, "name");
190 if (name_node && cJSON_IsString(name_node)) {
191 schema->name = local_strdup(cJSON_GetStringValue(name_node));
192 }
193
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);
197 }
198 } else {
199 /* primitive type name in object form */
200 schema->type = avro_type_from_string(type_str);
201 }
202 return schema;
203 }
204
205 /* fallback */
206 schema->type = AVRO_NULL;
207 return schema;
208}
209
210AVRO_SCHEMA* avro_schema_parse(const char* json_str) {
211 __n_assert(json_str, return NULL);
212
213 cJSON* json = cJSON_Parse(json_str);
214 __n_assert(json, return NULL);
215
216 AVRO_SCHEMA* schema = avro_schema_from_cjson(json);
217 cJSON_Delete(json);
218 return schema;
219}
220
221void avro_schema_free(AVRO_SCHEMA** schema_ptr) {
222 if (!schema_ptr || !*schema_ptr) return;
223
224 AVRO_SCHEMA* schema = *schema_ptr;
225
226 FreeNoLog(schema->name);
227 FreeNoLog(schema->namespace);
228
229 if (schema->fields) {
230 for (size_t i = 0; i < schema->nb_fields; i++) {
231 FreeNoLog(schema->fields[i].name);
232 avro_schema_free(&schema->fields[i].schema);
233 }
234 FreeNoLog(schema->fields);
235 }
236
237 avro_schema_free(&schema->items);
238 avro_schema_free(&schema->values);
239
240 if (schema->union_branches) {
241 for (size_t i = 0; i < schema->nb_branches; i++) {
242 avro_schema_free(&schema->union_branches[i]);
243 }
244 FreeNoLog(schema->union_branches);
245 }
246
247 if (schema->symbols) {
248 for (size_t i = 0; i < schema->nb_symbols; i++) {
249 FreeNoLog(schema->symbols[i]);
250 }
251 FreeNoLog(schema->symbols);
252 }
253
254 FreeNoLog(*schema_ptr);
255}
256
257char* avro_schema_to_json(const AVRO_SCHEMA* schema) {
258 __n_assert(schema, return NULL);
259
260 cJSON* json = NULL;
261
262 switch (schema->type) {
263 case AVRO_NULL:
264 case AVRO_BOOLEAN:
265 case AVRO_INT:
266 case AVRO_LONG:
267 case AVRO_FLOAT:
268 case AVRO_DOUBLE:
269 case AVRO_BYTES:
270 case AVRO_STRING:
271 /* for simple types used inside complex structures, return just the string */
272 json = cJSON_CreateString(avro_type_to_string(schema->type));
273 break;
274
275 case AVRO_RECORD: {
276 json = cJSON_CreateObject();
277 cJSON_AddStringToObject(json, "type", "record");
278 if (schema->name) {
279 cJSON_AddStringToObject(json, "name", schema->name);
280 }
281 if (schema->namespace) {
282 cJSON_AddStringToObject(json, "namespace", schema->namespace);
283 }
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);
288 char* ftype_str = avro_schema_to_json(schema->fields[i].schema);
289 if (ftype_str) {
290 cJSON* ftype = cJSON_Parse(ftype_str);
291 cJSON_AddItemToObject(field, "type", ftype);
292 Free(ftype_str);
293 }
294 cJSON_AddItemToArray(fields_arr, field);
295 }
296 break;
297 }
298
299 case AVRO_ARRAY: {
300 json = cJSON_CreateObject();
301 cJSON_AddStringToObject(json, "type", "array");
302 char* items_str = avro_schema_to_json(schema->items);
303 if (items_str) {
304 cJSON* items = cJSON_Parse(items_str);
305 cJSON_AddItemToObject(json, "items", items);
306 Free(items_str);
307 }
308 break;
309 }
310
311 case AVRO_MAP: {
312 json = cJSON_CreateObject();
313 cJSON_AddStringToObject(json, "type", "map");
314 char* values_str = avro_schema_to_json(schema->values);
315 if (values_str) {
316 cJSON* values = cJSON_Parse(values_str);
317 cJSON_AddItemToObject(json, "values", values);
318 Free(values_str);
319 }
320 break;
321 }
322
323 case AVRO_ENUM: {
324 json = cJSON_CreateObject();
325 cJSON_AddStringToObject(json, "type", "enum");
326 if (schema->name) {
327 cJSON_AddStringToObject(json, "name", schema->name);
328 }
329 if (schema->namespace) {
330 cJSON_AddStringToObject(json, "namespace", schema->namespace);
331 }
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]));
335 }
336 break;
337 }
338
339 case AVRO_UNION: {
340 json = cJSON_CreateArray();
341 for (size_t i = 0; i < schema->nb_branches; i++) {
342 char* branch_str = avro_schema_to_json(schema->union_branches[i]);
343 if (branch_str) {
344 cJSON* branch = cJSON_Parse(branch_str);
345 cJSON_AddItemToArray(json, branch);
346 Free(branch_str);
347 }
348 }
349 break;
350 }
351
352 case AVRO_FIXED: {
353 json = cJSON_CreateObject();
354 cJSON_AddStringToObject(json, "type", "fixed");
355 if (schema->name) {
356 cJSON_AddStringToObject(json, "name", schema->name);
357 }
358 cJSON_AddNumberToObject(json, "size", (double)schema->fixed_size);
359 break;
360 }
361 }
362
363 char* result = NULL;
364 if (json) {
365 result = cJSON_PrintUnformatted(json);
366 cJSON_Delete(json);
367 }
368 return result;
369}
370
371/* Zig-zag varint encoding/decoding */
372
373int avro_encode_long(N_STR** dest, int64_t value) {
374 __n_assert(dest, return FALSE);
375
376 /* zig-zag encode: portable sign-extension without signed shift UB */
377 uint64_t n = ((uint64_t)value << 1) ^ -(uint64_t)(value < 0);
378
379 unsigned char buf[10];
380 int len = 0;
381 while (n > 0x7F) {
382 buf[len++] = (unsigned char)((n & 0x7F) | 0x80);
383 n >>= 7;
384 }
385 buf[len++] = (unsigned char)n;
386
387 nstrcat_ex(dest, buf, (NSTRBYTE)len, 1);
388 return TRUE;
389}
390
391int avro_decode_long(AVRO_READER* reader, int64_t* value) {
392 __n_assert(reader, return FALSE);
393 __n_assert(value, return FALSE);
394
395 uint64_t n = 0;
396 int shift = 0;
397
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) {
402 /* zig-zag decode */
403 *value = (int64_t)((n >> 1) ^ (~(n & 1) + 1));
404 return TRUE;
405 }
406 shift += 7;
407 if (shift >= 70) {
408 n_log(LOG_ERR, "varint too long");
409 return FALSE;
410 }
411 }
412
413 n_log(LOG_ERR, "unexpected end of data while reading varint");
414 return FALSE;
415}
416
417/* Binary encoding */
418
420static int avro_encode_bytes_raw(N_STR** dest, const unsigned char* data, size_t len) {
421 int ret = avro_encode_long(dest, (int64_t)len);
422 if (ret != TRUE) return ret;
423 if (len > 0) {
424 nstrcat_ex(dest, (void*)data, (NSTRBYTE)len, 1);
425 }
426 return TRUE;
427}
428
430static int avro_find_union_branch(const AVRO_SCHEMA* schema, const cJSON* json) {
431 for (size_t i = 0; i < schema->nb_branches; i++) {
432 const AVRO_SCHEMA* branch = schema->union_branches[i];
433 switch (branch->type) {
434 case AVRO_NULL:
435 if (cJSON_IsNull(json)) return (int)i;
436 break;
437 case AVRO_BOOLEAN:
438 if (cJSON_IsBool(json)) return (int)i;
439 break;
440 case AVRO_INT:
441 case AVRO_LONG:
442 case AVRO_FLOAT:
443 case AVRO_DOUBLE:
444 if (cJSON_IsNumber(json)) return (int)i;
445 break;
446 case AVRO_STRING:
447 if (cJSON_IsString(json)) return (int)i;
448 break;
449 case AVRO_BYTES:
450 if (cJSON_IsString(json)) return (int)i;
451 break;
452 case AVRO_RECORD:
453 case AVRO_MAP:
454 if (cJSON_IsObject(json)) return (int)i;
455 break;
456 case AVRO_ARRAY:
457 if (cJSON_IsArray(json)) return (int)i;
458 break;
459 case AVRO_ENUM:
460 if (cJSON_IsString(json)) return (int)i;
461 break;
462 case AVRO_UNION:
463 case AVRO_FIXED:
464 break;
465 }
466 }
467 return -1;
468}
469
470int avro_encode_datum(N_STR** dest, const AVRO_SCHEMA* schema, const cJSON* json) {
471 __n_assert(dest, return FALSE);
472 __n_assert(schema, return FALSE);
473
474 switch (schema->type) {
475 case AVRO_NULL:
476 /* null is zero bytes */
477 return TRUE;
478
479 case AVRO_BOOLEAN: {
480 unsigned char b = cJSON_IsTrue(json) ? 1 : 0;
481 nstrcat_ex(dest, &b, 1, 1);
482 return TRUE;
483 }
484
485 case AVRO_INT: {
486 int32_t val = 0;
487 if (cJSON_IsNumber(json)) {
488 val = (int32_t)cJSON_GetNumberValue(json);
489 }
490 return avro_encode_long(dest, (int64_t)val);
491 }
492
493 case AVRO_LONG: {
494 int64_t val = 0;
495 if (cJSON_IsNumber(json)) {
496 val = (int64_t)cJSON_GetNumberValue(json);
497 }
498 return avro_encode_long(dest, val);
499 }
500
501 case AVRO_FLOAT: {
502 float val = 0.0f;
503 if (cJSON_IsNumber(json)) {
504 val = (float)cJSON_GetNumberValue(json);
505 }
506 /* Avro float is 4 bytes little-endian IEEE 754 */
507 unsigned char buf[4];
508 memcpy(buf, &val, 4);
509#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
510 unsigned char tmp;
511 tmp = buf[0];
512 buf[0] = buf[3];
513 buf[3] = tmp;
514 tmp = buf[1];
515 buf[1] = buf[2];
516 buf[2] = tmp;
517#endif
518 nstrcat_ex(dest, buf, 4, 1);
519 return TRUE;
520 }
521
522 case AVRO_DOUBLE: {
523 double val = 0.0;
524 if (cJSON_IsNumber(json)) {
525 val = cJSON_GetNumberValue(json);
526 }
527 /* Avro double is 8 bytes little-endian IEEE 754 */
528 unsigned char buf[8];
529 memcpy(buf, &val, 8);
530#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
531 unsigned char tmp;
532 tmp = buf[0];
533 buf[0] = buf[7];
534 buf[7] = tmp;
535 tmp = buf[1];
536 buf[1] = buf[6];
537 buf[6] = tmp;
538 tmp = buf[2];
539 buf[2] = buf[5];
540 buf[5] = tmp;
541 tmp = buf[3];
542 buf[3] = buf[4];
543 buf[4] = tmp;
544#endif
545 nstrcat_ex(dest, buf, 8, 1);
546 return TRUE;
547 }
548
549 case AVRO_BYTES: {
550 const char* str = cJSON_GetStringValue((cJSON*)json);
551 if (str) {
552 return avro_encode_bytes_raw(dest, (const unsigned char*)str, strlen(str));
553 }
554 return avro_encode_bytes_raw(dest, NULL, 0);
555 }
556
557 case AVRO_STRING: {
558 const char* str = cJSON_GetStringValue((cJSON*)json);
559 if (str) {
560 return avro_encode_bytes_raw(dest, (const unsigned char*)str, strlen(str));
561 }
562 return avro_encode_bytes_raw(dest, (const unsigned char*)"", 0);
563 }
564
565 case AVRO_RECORD: {
566 if (!cJSON_IsObject(json)) {
567 n_log(LOG_ERR, "expected JSON object for record type");
568 return FALSE;
569 }
570 for (size_t i = 0; i < schema->nb_fields; i++) {
571 const cJSON* field_val = cJSON_GetObjectItemCaseSensitive(json, schema->fields[i].name);
572 if (!field_val) {
573 /* try with a null placeholder */
574 cJSON null_val;
575 memset(&null_val, 0, sizeof(null_val));
576 null_val.type = cJSON_NULL;
577 if (avro_encode_datum(dest, schema->fields[i].schema, &null_val) != TRUE) {
578 return FALSE;
579 }
580 } else {
581 if (avro_encode_datum(dest, schema->fields[i].schema, field_val) != TRUE) {
582 return FALSE;
583 }
584 }
585 }
586 return TRUE;
587 }
588
589 case AVRO_ENUM: {
590 const char* sym = cJSON_GetStringValue((cJSON*)json);
591 if (!sym) {
592 n_log(LOG_ERR, "expected string for enum type");
593 return FALSE;
594 }
595 for (size_t i = 0; i < schema->nb_symbols; i++) {
596 if (strcmp(sym, schema->symbols[i]) == 0) {
597 return avro_encode_long(dest, (int64_t)i);
598 }
599 }
600 n_log(LOG_ERR, "unknown enum symbol: %s", sym);
601 return FALSE;
602 }
603
604 case AVRO_ARRAY: {
605 if (!cJSON_IsArray(json)) {
606 n_log(LOG_ERR, "expected JSON array for array type");
607 return FALSE;
608 }
609 int count = cJSON_GetArraySize(json);
610 if (count > 0) {
611 /* write block with count */
612 if (avro_encode_long(dest, (int64_t)count) != TRUE) return FALSE;
613 for (int i = 0; i < count; i++) {
614 if (avro_encode_datum(dest, schema->items, cJSON_GetArrayItem(json, i)) != TRUE) {
615 return FALSE;
616 }
617 }
618 }
619 /* terminating zero-count block */
620 return avro_encode_long(dest, 0);
621 }
622
623 case AVRO_MAP: {
624 if (!cJSON_IsObject(json)) {
625 n_log(LOG_ERR, "expected JSON object for map type");
626 return FALSE;
627 }
628 int count = cJSON_GetArraySize(json);
629 if (count > 0) {
630 if (avro_encode_long(dest, (int64_t)count) != TRUE) return FALSE;
631 const cJSON* item = NULL;
632 cJSON_ArrayForEach(item, json) {
633 /* encode key as string */
634 const char* key = item->string;
635 if (avro_encode_bytes_raw(dest, (const unsigned char*)key, strlen(key)) != TRUE) {
636 return FALSE;
637 }
638 /* encode value */
639 if (avro_encode_datum(dest, schema->values, item) != TRUE) {
640 return FALSE;
641 }
642 }
643 }
644 return avro_encode_long(dest, 0);
645 }
646
647 case AVRO_UNION: {
648 int branch_idx = avro_find_union_branch(schema, json);
649 if (branch_idx < 0) {
650 n_log(LOG_ERR, "no matching union branch for JSON value");
651 return FALSE;
652 }
653 if (avro_encode_long(dest, (int64_t)branch_idx) != TRUE) return FALSE;
654 return avro_encode_datum(dest, schema->union_branches[branch_idx], json);
655 }
656
657 case AVRO_FIXED: {
658 const char* str = cJSON_GetStringValue((cJSON*)json);
659 if (!str) {
660 n_log(LOG_ERR, "expected string for fixed type");
661 return FALSE;
662 }
663 size_t len = strlen(str);
664 if (len > schema->fixed_size) {
665 len = schema->fixed_size;
666 }
667 /* write exactly fixed_size bytes, pad with zeros if needed */
668 nstrcat_ex(dest, (void*)str, (NSTRBYTE)len, 1);
669 if (len < schema->fixed_size) {
670 size_t pad = schema->fixed_size - len;
671 unsigned char* zeros = NULL;
672 Malloc(zeros, unsigned char, pad);
673 if (zeros) {
674 nstrcat_ex(dest, zeros, (NSTRBYTE)pad, 1);
675 Free(zeros);
676 }
677 }
678 return TRUE;
679 }
680 }
681
682 return FALSE;
683}
684
685/* Binary decoding */
686
688static int avro_decode_bytes_raw(AVRO_READER* reader, unsigned char** out, size_t* out_len) {
689 int64_t len = 0;
690 if (avro_decode_long(reader, &len) != TRUE) return FALSE;
691 if (len < 0) {
692 n_log(LOG_ERR, "negative byte length: %" PRId64, len);
693 return FALSE;
694 }
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);
698 return FALSE;
699 }
700 Malloc(*out, unsigned char, (size_t)len + 1);
701 __n_assert(*out, return FALSE);
702 if (len > 0) {
703 memcpy(*out, reader->data + reader->pos, (size_t)len);
704 }
705 (*out)[(size_t)len] = '\0';
706 reader->pos += (size_t)len;
707 return TRUE;
708}
709
710cJSON* avro_decode_datum(AVRO_READER* reader, const AVRO_SCHEMA* schema) {
711 __n_assert(reader, return NULL);
712 __n_assert(schema, return NULL);
713
714 switch (schema->type) {
715 case AVRO_NULL:
716 return cJSON_CreateNull();
717
718 case AVRO_BOOLEAN: {
719 if (reader->pos >= reader->size) {
720 n_log(LOG_ERR, "unexpected end of data reading boolean");
721 return NULL;
722 }
723 unsigned char b = reader->data[reader->pos++];
724 return cJSON_CreateBool(b != 0);
725 }
726
727 case AVRO_INT: {
728 int64_t val = 0;
729 if (avro_decode_long(reader, &val) != TRUE) return NULL;
730 return cJSON_CreateNumber((double)(int32_t)val);
731 }
732
733 case AVRO_LONG: {
734 int64_t val = 0;
735 if (avro_decode_long(reader, &val) != TRUE) return NULL;
736 return cJSON_CreateNumber((double)val);
737 }
738
739 case AVRO_FLOAT: {
740 if (reader->pos + 4 > reader->size) {
741 n_log(LOG_ERR, "unexpected end of data reading float");
742 return NULL;
743 }
744 unsigned char buf[4];
745 memcpy(buf, reader->data + reader->pos, 4);
746 reader->pos += 4;
747#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
748 unsigned char tmp;
749 tmp = buf[0];
750 buf[0] = buf[3];
751 buf[3] = tmp;
752 tmp = buf[1];
753 buf[1] = buf[2];
754 buf[2] = tmp;
755#endif
756 float val;
757 memcpy(&val, buf, 4);
758 return cJSON_CreateNumber((double)val);
759 }
760
761 case AVRO_DOUBLE: {
762 if (reader->pos + 8 > reader->size) {
763 n_log(LOG_ERR, "unexpected end of data reading double");
764 return NULL;
765 }
766 unsigned char buf[8];
767 memcpy(buf, reader->data + reader->pos, 8);
768 reader->pos += 8;
769#if BYTEORDER_ENDIAN == BYTEORDER_BIG_ENDIAN
770 unsigned char tmp;
771 tmp = buf[0];
772 buf[0] = buf[7];
773 buf[7] = tmp;
774 tmp = buf[1];
775 buf[1] = buf[6];
776 buf[6] = tmp;
777 tmp = buf[2];
778 buf[2] = buf[5];
779 buf[5] = tmp;
780 tmp = buf[3];
781 buf[3] = buf[4];
782 buf[4] = tmp;
783#endif
784 double val;
785 memcpy(&val, buf, 8);
786 return cJSON_CreateNumber(val);
787 }
788
789 case AVRO_BYTES: {
790 unsigned char* data = NULL;
791 size_t len = 0;
792 if (avro_decode_bytes_raw(reader, &data, &len) != TRUE) return NULL;
793 cJSON* result = cJSON_CreateString((const char*)data);
794 Free(data);
795 return result;
796 }
797
798 case AVRO_STRING: {
799 unsigned char* data = NULL;
800 size_t len = 0;
801 if (avro_decode_bytes_raw(reader, &data, &len) != TRUE) return NULL;
802 cJSON* result = cJSON_CreateString((const char*)data);
803 Free(data);
804 return result;
805 }
806
807 case AVRO_RECORD: {
808 cJSON* obj = cJSON_CreateObject();
809 for (size_t i = 0; i < schema->nb_fields; i++) {
810 cJSON* field_val = avro_decode_datum(reader, schema->fields[i].schema);
811 if (!field_val) {
812 cJSON_Delete(obj);
813 return NULL;
814 }
815 cJSON_AddItemToObject(obj, schema->fields[i].name, field_val);
816 }
817 return obj;
818 }
819
820 case AVRO_ENUM: {
821 int64_t idx = 0;
822 if (avro_decode_long(reader, &idx) != TRUE) return NULL;
823 if (idx < 0 || (size_t)idx >= schema->nb_symbols) {
824 n_log(LOG_ERR, "enum index %" PRId64 " out of range (0-%zu)", idx, schema->nb_symbols - 1);
825 return NULL;
826 }
827 return cJSON_CreateString(schema->symbols[(size_t)idx]);
828 }
829
830 case AVRO_ARRAY: {
831 cJSON* arr = cJSON_CreateArray();
832 int64_t block_count = 0;
833 for (;;) {
834 if (avro_decode_long(reader, &block_count) != TRUE) {
835 cJSON_Delete(arr);
836 return NULL;
837 }
838 if (block_count == 0) break;
839 if (block_count < 0) {
840 /* negative count means block size follows */
841 block_count = -block_count;
842 int64_t block_size = 0;
843 if (avro_decode_long(reader, &block_size) != TRUE) {
844 cJSON_Delete(arr);
845 return NULL;
846 }
847 (void)block_size;
848 }
849 for (int64_t i = 0; i < block_count; i++) {
850 cJSON* item = avro_decode_datum(reader, schema->items);
851 if (!item) {
852 cJSON_Delete(arr);
853 return NULL;
854 }
855 cJSON_AddItemToArray(arr, item);
856 }
857 }
858 return arr;
859 }
860
861 case AVRO_MAP: {
862 cJSON* obj = cJSON_CreateObject();
863 int64_t block_count = 0;
864 for (;;) {
865 if (avro_decode_long(reader, &block_count) != TRUE) {
866 cJSON_Delete(obj);
867 return NULL;
868 }
869 if (block_count == 0) break;
870 if (block_count < 0) {
871 block_count = -block_count;
872 int64_t block_size = 0;
873 if (avro_decode_long(reader, &block_size) != TRUE) {
874 cJSON_Delete(obj);
875 return NULL;
876 }
877 (void)block_size;
878 }
879 for (int64_t i = 0; i < block_count; i++) {
880 /* decode key */
881 unsigned char* key = NULL;
882 size_t key_len = 0;
883 if (avro_decode_bytes_raw(reader, &key, &key_len) != TRUE) {
884 cJSON_Delete(obj);
885 return NULL;
886 }
887 /* decode value */
888 cJSON* val = avro_decode_datum(reader, schema->values);
889 if (!val) {
890 Free(key);
891 cJSON_Delete(obj);
892 return NULL;
893 }
894 cJSON_AddItemToObject(obj, (const char*)key, val);
895 Free(key);
896 }
897 }
898 return obj;
899 }
900
901 case AVRO_UNION: {
902 int64_t branch_idx = 0;
903 if (avro_decode_long(reader, &branch_idx) != TRUE) return NULL;
904 if (branch_idx < 0 || (size_t)branch_idx >= schema->nb_branches) {
905 n_log(LOG_ERR, "union branch index %" PRId64 " out of range (0-%zu)", branch_idx, schema->nb_branches - 1);
906 return NULL;
907 }
908 return avro_decode_datum(reader, schema->union_branches[(size_t)branch_idx]);
909 }
910
911 case AVRO_FIXED: {
912 if (reader->pos + schema->fixed_size > reader->size) {
913 n_log(LOG_ERR, "unexpected end of data reading fixed(%zu)", schema->fixed_size);
914 return NULL;
915 }
916 char* buf = NULL;
917 Malloc(buf, char, schema->fixed_size + 1);
918 __n_assert(buf, return NULL);
919 memcpy(buf, reader->data + reader->pos, schema->fixed_size);
920 buf[schema->fixed_size] = '\0';
921 reader->pos += schema->fixed_size;
922 cJSON* result = cJSON_CreateString(buf);
923 Free(buf);
924 return result;
925 }
926 }
927
928 return NULL;
929}
930
931/* Object Container File format */
932
933N_STR* avro_encode_container(const AVRO_SCHEMA* schema, const cJSON* records) {
934 __n_assert(schema, return NULL);
935 __n_assert(records, return NULL);
936
937 if (!cJSON_IsArray(records)) {
938 n_log(LOG_ERR, "records must be a JSON array");
939 return NULL;
940 }
941
942 N_STR* output = NULL;
943 nstrprintf(output, "%s", "");
944 __n_assert(output, return NULL);
945
946 /* 1. Magic bytes */
947 nstrcat_ex(&output, (void*)AVRO_MAGIC, AVRO_MAGIC_LEN, 1);
948
949 /* 2. File metadata as Avro map */
950 char* schema_json = avro_schema_to_json(schema);
951 __n_assert(schema_json, free_nstr(&output); return NULL);
952
953 /* metadata map: 1 block with 2 entries, then 0 block */
954 /* block count = 2 (avro.schema + avro.codec) */
955 avro_encode_long(&output, 2);
956
957 /* entry 1: avro.schema */
958 const char* key1 = "avro.schema";
959 avro_encode_long(&output, (int64_t)strlen(key1));
960 nstrcat_ex(&output, (void*)key1, (NSTRBYTE)strlen(key1), 1);
961 avro_encode_long(&output, (int64_t)strlen(schema_json));
962 nstrcat_ex(&output, (void*)schema_json, (NSTRBYTE)strlen(schema_json), 1);
963
964 /* entry 2: avro.codec = "null" (no compression) */
965 const char* key2 = "avro.codec";
966 avro_encode_long(&output, (int64_t)strlen(key2));
967 nstrcat_ex(&output, (void*)key2, (NSTRBYTE)strlen(key2), 1);
968 const char* codec = "null";
969 avro_encode_long(&output, (int64_t)strlen(codec));
970 nstrcat_ex(&output, (void*)codec, (NSTRBYTE)strlen(codec), 1);
971
972 /* end of metadata map */
973 avro_encode_long(&output, 0);
974
975 Free(schema_json);
976
977 /* 3. Generate sync marker (16 bytes) */
978 unsigned char sync[AVRO_SYNC_LEN];
979 srand((unsigned)time(NULL));
980 for (int i = 0; i < AVRO_SYNC_LEN; i++) {
981 sync[i] = (unsigned char)(rand() % 256);
982 }
983 nstrcat_ex(&output, sync, AVRO_SYNC_LEN, 1);
984
985 /* 4. Data block: encode all records into a single block */
986 int count = cJSON_GetArraySize(records);
987
988 N_STR* block_data = NULL;
989 nstrprintf(block_data, "%s", "");
990 __n_assert(block_data, free_nstr(&output); return NULL);
991
992 for (int i = 0; i < count; i++) {
993 const cJSON* record = cJSON_GetArrayItem(records, i);
994 if (avro_encode_datum(&block_data, schema, record) != TRUE) {
995 n_log(LOG_ERR, "failed to encode record %d", i);
996 free_nstr(&block_data);
997 free_nstr(&output);
998 return NULL;
999 }
1000 }
1001
1002 /* write block header: object count, then byte size of serialized data */
1003 avro_encode_long(&output, (int64_t)count);
1004 avro_encode_long(&output, (int64_t)block_data->written);
1005
1006 /* write serialized data */
1007 nstrcat_ex(&output, block_data->data, (NSTRBYTE)block_data->written, 1);
1008 free_nstr(&block_data);
1009
1010 /* write sync marker */
1011 nstrcat_ex(&output, sync, AVRO_SYNC_LEN, 1);
1012
1013 return output;
1014}
1015
1016cJSON* avro_decode_container(const AVRO_SCHEMA* schema, const N_STR* avro_data) {
1017 __n_assert(schema, return NULL);
1018 __n_assert(avro_data, return NULL);
1019 __n_assert(avro_data->data, return NULL);
1020
1021 AVRO_READER reader;
1022 reader.data = (const unsigned char*)avro_data->data;
1023 reader.size = avro_data->written;
1024 reader.pos = 0;
1025
1026 /* 1. Check magic */
1027 if (reader.size < AVRO_MAGIC_LEN + AVRO_SYNC_LEN) {
1028 n_log(LOG_ERR, "data too small for Avro container");
1029 return NULL;
1030 }
1031 if (memcmp(reader.data, AVRO_MAGIC, AVRO_MAGIC_LEN) != 0) {
1032 n_log(LOG_ERR, "invalid Avro magic bytes");
1033 return NULL;
1034 }
1035 reader.pos = AVRO_MAGIC_LEN;
1036
1037 /* 2. Read metadata map (skip it, we use the provided schema) */
1038 int64_t block_count = 0;
1039 for (;;) {
1040 if (avro_decode_long(&reader, &block_count) != TRUE) return NULL;
1041 if (block_count == 0) break;
1042 if (block_count < 0) {
1043 /* negative count: skip block_size bytes */
1044 int64_t block_size = 0;
1045 if (avro_decode_long(&reader, &block_size) != TRUE) return NULL;
1046 if (block_size < 0 || (size_t)block_size > reader.size - reader.pos) {
1047 n_log(LOG_ERR, "invalid metadata block size");
1048 return NULL;
1049 }
1050 reader.pos += (size_t)block_size;
1051 continue;
1052 }
1053 /* positive count: read key-value pairs */
1054 for (int64_t i = 0; i < block_count; i++) {
1055 unsigned char* key = NULL;
1056 size_t key_len = 0;
1057 if (avro_decode_bytes_raw(&reader, &key, &key_len) != TRUE) return NULL;
1058 Free(key);
1059 unsigned char* val = NULL;
1060 size_t val_len = 0;
1061 if (avro_decode_bytes_raw(&reader, &val, &val_len) != TRUE) return NULL;
1062 Free(val);
1063 }
1064 }
1065
1066 /* 3. Read sync marker */
1067 if (reader.pos + AVRO_SYNC_LEN > reader.size) {
1068 n_log(LOG_ERR, "unexpected end of data reading sync marker");
1069 return NULL;
1070 }
1071 unsigned char sync[AVRO_SYNC_LEN];
1072 memcpy(sync, reader.data + reader.pos, AVRO_SYNC_LEN);
1073 reader.pos += AVRO_SYNC_LEN;
1074
1075 /* 4. Read data blocks */
1076 cJSON* all_records = cJSON_CreateArray();
1077
1078 while (reader.pos < reader.size) {
1079 int64_t obj_count = 0;
1080 if (avro_decode_long(&reader, &obj_count) != TRUE) {
1081 cJSON_Delete(all_records);
1082 return NULL;
1083 }
1084 if (obj_count <= 0) break;
1085
1086 int64_t block_byte_size = 0;
1087 if (avro_decode_long(&reader, &block_byte_size) != TRUE) {
1088 cJSON_Delete(all_records);
1089 return NULL;
1090 }
1091
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);
1095 return NULL;
1096 }
1097
1098 size_t block_end = reader.pos + (size_t)block_byte_size;
1099
1100 for (int64_t i = 0; i < obj_count; i++) {
1101 cJSON* record = avro_decode_datum(&reader, schema);
1102 if (!record) {
1103 n_log(LOG_ERR, "failed to decode record %" PRId64, i);
1104 cJSON_Delete(all_records);
1105 return NULL;
1106 }
1107 cJSON_AddItemToArray(all_records, record);
1108 }
1109
1110 /* verify we consumed exactly the block */
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;
1114 }
1115
1116 /* read and verify sync marker */
1117 if (reader.pos + AVRO_SYNC_LEN > reader.size) {
1118 n_log(LOG_ERR, "unexpected end of data reading block sync marker");
1119 cJSON_Delete(all_records);
1120 return NULL;
1121 }
1122 if (memcmp(reader.data + reader.pos, sync, AVRO_SYNC_LEN) != 0) {
1123 n_log(LOG_ERR, "sync marker mismatch");
1124 cJSON_Delete(all_records);
1125 return NULL;
1126 }
1127 reader.pos += AVRO_SYNC_LEN;
1128 }
1129
1130 return all_records;
1131}
1132
1133/* File-level convenience functions */
1134
1135int avro_json_to_file(const char* avro_filename, const char* schema_filename, const char* json_filename) {
1136 __n_assert(avro_filename, return FALSE);
1137 __n_assert(schema_filename, return FALSE);
1138 __n_assert(json_filename, return FALSE);
1139
1140 /* load schema file */
1141 N_STR* schema_str = file_to_nstr((char*)schema_filename);
1142 __n_assert(schema_str, return FALSE);
1143
1144 AVRO_SCHEMA* schema = avro_schema_parse(schema_str->data);
1145 free_nstr(&schema_str);
1146 __n_assert(schema, return FALSE);
1147
1148 /* load JSON file */
1149 N_STR* json_str = file_to_nstr((char*)json_filename);
1150 if (!json_str) {
1151 avro_schema_free(&schema);
1152 return FALSE;
1153 }
1154
1155 cJSON* json = cJSON_Parse(json_str->data);
1156 free_nstr(&json_str);
1157 if (!json) {
1158 n_log(LOG_ERR, "failed to parse JSON file %s: %s", json_filename, _str(cJSON_GetErrorPtr()));
1159 avro_schema_free(&schema);
1160 return FALSE;
1161 }
1162
1163 /* if json is a single object, wrap it in an array */
1164 cJSON* records = json;
1165 int wrapped = 0;
1166 if (cJSON_IsObject(json)) {
1167 records = cJSON_CreateArray();
1168 cJSON_AddItemToArray(records, cJSON_Duplicate(json, 1));
1169 wrapped = 1;
1170 }
1171
1172 /* encode to Avro container */
1173 N_STR* avro_data = avro_encode_container(schema, records);
1174
1175 if (wrapped) {
1176 cJSON_Delete(records);
1177 }
1178 cJSON_Delete(json);
1179 avro_schema_free(&schema);
1180
1181 if (!avro_data) {
1182 n_log(LOG_ERR, "failed to encode Avro container");
1183 return FALSE;
1184 }
1185
1186 /* write Avro file */
1187 int ret = nstr_to_file(avro_data, (char*)avro_filename);
1188 free_nstr(&avro_data);
1189
1190 if (ret != TRUE) {
1191 n_log(LOG_ERR, "failed to write Avro file %s", avro_filename);
1192 return FALSE;
1193 }
1194
1195 n_log(LOG_INFO, "wrote Avro file %s", avro_filename);
1196 return TRUE;
1197}
1198
1199int avro_file_to_json(const char* avro_filename, const char* schema_filename, const char* json_filename) {
1200 __n_assert(avro_filename, return FALSE);
1201 __n_assert(schema_filename, return FALSE);
1202 __n_assert(json_filename, return FALSE);
1203
1204 /* load schema file */
1205 N_STR* schema_str = file_to_nstr((char*)schema_filename);
1206 __n_assert(schema_str, return FALSE);
1207
1208 AVRO_SCHEMA* schema = avro_schema_parse(schema_str->data);
1209 free_nstr(&schema_str);
1210 __n_assert(schema, return FALSE);
1211
1212 /* load Avro file */
1213 N_STR* avro_data = file_to_nstr((char*)avro_filename);
1214 if (!avro_data) {
1215 avro_schema_free(&schema);
1216 return FALSE;
1217 }
1218
1219 /* decode from Avro container */
1220 cJSON* records = avro_decode_container(schema, avro_data);
1221 free_nstr(&avro_data);
1222 avro_schema_free(&schema);
1223
1224 if (!records) {
1225 n_log(LOG_ERR, "failed to decode Avro file %s", avro_filename);
1226 return FALSE;
1227 }
1228
1229 /* write JSON file */
1230 char* json_output = cJSON_Print(records);
1231 cJSON_Delete(records);
1232
1233 if (!json_output) {
1234 n_log(LOG_ERR, "failed to serialize JSON");
1235 return FALSE;
1236 }
1237
1238 N_STR* json_nstr = char_to_nstr(json_output);
1239 free(json_output);
1240
1241 if (!json_nstr) {
1242 return FALSE;
1243 }
1244
1245 int ret = nstr_to_file(json_nstr, (char*)json_filename);
1246 free_nstr(&json_nstr);
1247
1248 if (ret != TRUE) {
1249 n_log(LOG_ERR, "failed to write JSON file %s", json_filename);
1250 return FALSE;
1251 }
1252
1253 n_log(LOG_INFO, "wrote JSON file %s", json_filename);
1254 return TRUE;
1255}
1256
1257/* N_STR in-memory convenience functions */
1258
1260 __n_assert(schema_nstr, return NULL);
1261 __n_assert(schema_nstr->data, return NULL);
1262
1263 return avro_schema_parse(schema_nstr->data);
1264}
1265
1266N_STR* avro_nstr_json_to_avro(const N_STR* schema_nstr, const N_STR* json_nstr) {
1267 __n_assert(schema_nstr, return NULL);
1268 __n_assert(schema_nstr->data, return NULL);
1269 __n_assert(json_nstr, return NULL);
1270 __n_assert(json_nstr->data, return NULL);
1271
1272 AVRO_SCHEMA* schema = avro_schema_parse(schema_nstr->data);
1273 __n_assert(schema, return NULL);
1274
1275 cJSON* json = cJSON_Parse(json_nstr->data);
1276 if (!json) {
1277 n_log(LOG_ERR, "failed to parse JSON: %s", _str(cJSON_GetErrorPtr()));
1278 avro_schema_free(&schema);
1279 return NULL;
1280 }
1281
1282 /* if json is a single object, wrap it in an array */
1283 cJSON* records = json;
1284 int wrapped = 0;
1285 if (cJSON_IsObject(json)) {
1286 records = cJSON_CreateArray();
1287 cJSON_AddItemToArray(records, cJSON_Duplicate(json, 1));
1288 wrapped = 1;
1289 }
1290
1291 N_STR* avro_data = avro_encode_container(schema, records);
1292
1293 if (wrapped) {
1294 cJSON_Delete(records);
1295 }
1296 cJSON_Delete(json);
1297 avro_schema_free(&schema);
1298
1299 return avro_data;
1300}
1301
1302N_STR* avro_nstr_avro_to_json(const N_STR* schema_nstr, const N_STR* avro_nstr) {
1303 __n_assert(schema_nstr, return NULL);
1304 __n_assert(schema_nstr->data, return NULL);
1305 __n_assert(avro_nstr, return NULL);
1306 __n_assert(avro_nstr->data, return NULL);
1307
1308 AVRO_SCHEMA* schema = avro_schema_parse(schema_nstr->data);
1309 __n_assert(schema, return NULL);
1310
1311 cJSON* records = avro_decode_container(schema, avro_nstr);
1312 avro_schema_free(&schema);
1313
1314 if (!records) {
1315 n_log(LOG_ERR, "failed to decode Avro container");
1316 return NULL;
1317 }
1318
1319 char* json_output = cJSON_Print(records);
1320 cJSON_Delete(records);
1321
1322 if (!json_output) {
1323 n_log(LOG_ERR, "failed to serialize JSON");
1324 return NULL;
1325 }
1326
1327 N_STR* result = char_to_nstr(json_output);
1328 free(json_output);
1329
1330 return result;
1331}
char * key
size_t nb_symbols
number of enum symbols
Definition n_avro.h:107
char * name
field name
Definition n_avro.h:79
AVRO_SCHEMA ** union_branches
union branch schemas
Definition n_avro.h:101
char * name
name (for record, enum, fixed)
Definition n_avro.h:89
AVRO_SCHEMA * items
item schema (for array)
Definition n_avro.h:97
AVRO_SCHEMA * values
value schema (for map)
Definition n_avro.h:99
AVRO_FIELD * fields
namespace (for record, enum)
Definition n_avro.h:91
size_t pos
current position
Definition n_avro.h:119
size_t fixed_size
fixed size
Definition n_avro.h:109
AVRO_SCHEMA * schema
field schema
Definition n_avro.h:81
AVRO_TYPE type
schema type
Definition n_avro.h:87
const unsigned char * data
data buffer
Definition n_avro.h:115
size_t nb_branches
number of union branches
Definition n_avro.h:103
char ** symbols
enum symbols
Definition n_avro.h:105
size_t nb_fields
number of fields (for record)
Definition n_avro.h:95
size_t size
total size
Definition n_avro.h:117
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)
Definition n_avro.c:1266
#define AVRO_SYNC_LEN
Avro sync marker length.
Definition n_avro.h:53
AVRO_TYPE
Avro schema type enumeration.
Definition n_avro.h:56
#define AVRO_MAGIC_LEN
Avro magic length.
Definition n_avro.h:51
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.
Definition n_avro.c:1199
N_STR * avro_encode_container(const AVRO_SCHEMA *schema, const cJSON *records)
Encode a cJSON array of records into Avro container format N_STR.
Definition n_avro.c:933
AVRO_SCHEMA * avro_schema_from_cjson(const cJSON *json)
Parse an Avro schema from a cJSON object.
Definition n_avro.c:72
int avro_encode_datum(N_STR **dest, const AVRO_SCHEMA *schema, const cJSON *json)
Encode a cJSON value as Avro binary according to schema.
Definition n_avro.c:470
AVRO_SCHEMA * avro_schema_parse_nstr(const N_STR *schema_nstr)
Parse schema from N_STR.
Definition n_avro.c:1259
AVRO_SCHEMA * avro_schema_parse(const char *json_str)
Parse an Avro schema from a JSON string.
Definition n_avro.c:210
int avro_decode_long(AVRO_READER *reader, int64_t *value)
Decode a zig-zag varint from reader into a 64-bit signed integer.
Definition n_avro.c:391
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.
Definition n_avro.c:1016
cJSON * avro_decode_datum(AVRO_READER *reader, const AVRO_SCHEMA *schema)
Decode an Avro binary datum into cJSON according to schema.
Definition n_avro.c:710
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)
Definition n_avro.c:1302
char * avro_schema_to_json(const AVRO_SCHEMA *schema)
Convert an Avro schema back to JSON string (caller must free)
Definition n_avro.c:257
void avro_schema_free(AVRO_SCHEMA **schema_ptr)
Free an Avro schema.
Definition n_avro.c:221
int avro_encode_long(N_STR **dest, int64_t value)
Encode a 64-bit signed integer as zig-zag varint into N_STR.
Definition n_avro.c:373
#define AVRO_MAGIC
Avro object container file magic bytes.
Definition n_avro.h:49
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.
Definition n_avro.c:1135
@ AVRO_ENUM
Definition n_avro.h:66
@ AVRO_MAP
Definition n_avro.h:68
@ AVRO_FIXED
Definition n_avro.h:70
@ AVRO_UNION
Definition n_avro.h:69
@ AVRO_ARRAY
Definition n_avro.h:67
@ AVRO_FLOAT
Definition n_avro.h:61
@ AVRO_DOUBLE
Definition n_avro.h:62
@ AVRO_BOOLEAN
Definition n_avro.h:58
@ AVRO_INT
Definition n_avro.h:59
@ AVRO_STRING
Definition n_avro.h:64
@ AVRO_LONG
Definition n_avro.h:60
@ AVRO_NULL
Definition n_avro.h:57
@ AVRO_BYTES
Definition n_avro.h:63
@ AVRO_RECORD
Definition n_avro.h:65
Avro schema field (for records)
Definition n_avro.h:77
Avro read cursor for decoding.
Definition n_avro.h:113
Avro schema definition.
Definition n_avro.h:85
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define _str(__PTR)
define true
Definition n_common.h:193
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_ERR
error conditions
Definition n_log.h:76
#define LOG_WARNING
warning conditions
Definition n_log.h:78
#define LOG_INFO
informational
Definition n_log.h:82
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
Definition n_str.h:68
char * data
the string
Definition n_str.h:63
size_t NSTRBYTE
N_STR base unit.
Definition n_str.h:58
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
#define local_strdup(__src_)
Do tar(1) matching rules, which ignore a trailing slash?
Definition n_str.h:79
int nstr_to_file(N_STR *str, char *filename)
Write a N_STR content into a file.
Definition n_str.c:424
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.
Definition n_str.c:1089
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
Definition n_str.c:255
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
Definition n_str.h:117
N_STR * file_to_nstr(char *filename)
Load a whole file into a N_STR.
Definition n_str.c:288
A box including a string and his lenght.
Definition n_str.h:61
static int avro_encode_bytes_raw(N_STR **dest, const unsigned char *data, size_t len)
helper: encode raw bytes with length prefix
Definition n_avro.c:420
static AVRO_TYPE avro_type_from_string(const char *type_str)
helper to get AVRO_TYPE from a type name string
Definition n_avro.c:36
static int avro_decode_bytes_raw(AVRO_READER *reader, unsigned char **out, size_t *out_len)
helper: read raw bytes with length prefix
Definition n_avro.c:688
static const char * avro_type_to_string(AVRO_TYPE type)
helper to get type name from AVRO_TYPE
Definition n_avro.c:49
static int avro_find_union_branch(const AVRO_SCHEMA *schema, const cJSON *json)
helper: find which union branch matches a cJSON value
Definition n_avro.c:430
Avro binary format encoding/decoding with JSON conversion.