From 08bb217747f6bc86d6195203904854927ed4e183 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Sat, 20 Sep 2025 23:15:04 -0500 Subject: [PATCH] Implement JSON printing function --- .gitignore | 2 + examples/empack_cli.c | 50 ++++++++ src/empack.c | 259 +++++++++++++++++++++++++++++++++++++++--- src/empack.h | 31 ++++- 4 files changed, 324 insertions(+), 18 deletions(-) create mode 100644 .gitignore create mode 100644 examples/empack_cli.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fdaad8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +examples/empack_cli diff --git a/examples/empack_cli.c b/examples/empack_cli.c new file mode 100644 index 0000000..eef3c67 --- /dev/null +++ b/examples/empack_cli.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +#include "empack.h" + +#define USAGE \ +"empack_cli [OPTIONS] \r\n" \ +" file: File to read binary Msgpack data from, \"-\" for stdin\r\n" \ +" msgpack hex: Hex string of Msgpack data\r\n" \ +"\r\n" \ +"OPTIONS\r\n" \ +" -h/--help: Print this message\r\n" + +static void +print_func(const char *buf, uint32_t buflen, void *arg) +{ + printf("%.*s", buflen, buf); +} + +int +main(int argc, char **argv) +{ + const uint8_t input[] = { 0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00 }; + size_t input_len = sizeof(input); + empack_t empack; + uint8_t arena[256]; + uint32_t chars_printed; + + empack_init(&empack, arena, sizeof(arena)); + + if (empack_parse(&empack, input, input_len)) { + fprintf(stderr, "Parsing error\r\n"); + return (-1); + } + + chars_printed = empack_print(&empack, print_func, NULL, " "); + printf("\r\n-- Stats --\r\n" + "Characters Printed: %u\r\n" + "Arena Memory Used: %u/%uB (%u%%)\r\n" + "-----------\r\n", + chars_printed, + empack.arena_used, + empack.arena_sz, + (empack.arena_used * 100) / empack.arena_sz); + + return (0); +} diff --git a/src/empack.c b/src/empack.c index 5d2a931..2e6dd6d 100644 --- a/src/empack.c +++ b/src/empack.c @@ -1,3 +1,4 @@ +#include #include #include @@ -39,6 +40,7 @@ empack_init(empack_t *empack, void *arena, uint32_t arena_sz) empack->arena_used = 0; memset(arena, 0, arena_sz); + memset(&empack->root, 0, sizeof(empack->root)); } empack_status_e @@ -62,16 +64,17 @@ empack_parse(empack_t *empack, const void *msgpack, uint32_t len) { empack_array_t *parr = &parent->val.array; - if (parr->object_count == parr->object_count_real) { + if (parr->object_count == parr->objects_parsed) { ret = EMPACK_STATUS_ERROR; goto empack_parse_return; } - cur = &parr->objects[parr->object_count_real]; + cur->next = &parr->objects[parr->objects_parsed]; + cur = cur->next; cur->parent = parent; - parr->object_count_real++; - if (parr->object_count == parr->object_count_real) { + parr->objects_parsed++; + if (parr->object_count == parr->objects_parsed) { parent = parent->parent; } break; @@ -82,21 +85,22 @@ empack_parse(empack_t *empack, const void *msgpack, uint32_t len) { empack_map_t *pmap = &parent->val.map; - if (pmap->pair_count == pmap->pair_count_real) { + if (pmap->pair_count == pmap->pairs_parsed) { ret = EMPACK_STATUS_ERROR; goto empack_parse_return; } - if (pmap->pairs[pmap->pair_count_real].key.type == EMPACK_TYPE_ERROR) { + if (pmap->pairs[pmap->pairs_parsed].key.type == EMPACK_TYPE_ERROR) { /* We haven't parsed the key yet */ - cur = &pmap->pairs[pmap->pair_count_real].key; + cur->next = &pmap->pairs[pmap->pairs_parsed].key; } else { - cur = &pmap->pairs[pmap->pair_count_real].value; - pmap->pair_count_real++; + cur->next = &pmap->pairs[pmap->pairs_parsed].value; + pmap->pairs_parsed++; } + cur = cur->next; cur->parent = parent; - if (pmap->pair_count == pmap->pair_count_real) { + if (pmap->pair_count == pmap->pairs_parsed) { parent = parent->parent; } break; @@ -117,9 +121,6 @@ empack_parse(empack_t *empack, const void *msgpack, uint32_t len) p += len_used; tokens_needed--; - if ((tokens_needed == 0) && (ret == EMPACK_STATUS_EXTRA)) { - goto empack_parse_return; - } switch (cur->type) { @@ -166,6 +167,10 @@ empack_parse(empack_t *empack, const void *msgpack, uint32_t len) default: break; } + + if ((tokens_needed == 0) && (ret == EMPACK_STATUS_EXTRA)) { + goto empack_parse_return; + } } /* prioritize errors over incomplete status */ @@ -184,7 +189,7 @@ empack_parse_one(empack_obj_t *object, const void *msgpack, uint32_t *len) uint32_t p = 0; empack_status_e ret = EMPACK_STATUS_EXTRA; - memset(object, 0, sizeof(*object)); + memset(&object->val, 0, sizeof(object->val)); #define EMPACK_PARSEONE_ASSERT(need) do { \ if ((p + (need)) > (*len)) { \ @@ -440,7 +445,10 @@ empack_parse_one(empack_obj_t *object, const void *msgpack, uint32_t *len) } else if ((object->type >= EMPACK_TYPE_FIXSTR) && (object->type <= EMPACK_FIXSTR_MAX)) { object->val.str.size = ((uint8_t)object->type) & 0x1f; + EMPACK_PARSEONE_ASSERT(object->val.str.size); + object->val.str.data = (const char *)&buf[p]; object->type = EMPACK_TYPE_FIXSTR; + p += object->val.str.size; } else if ((object->type >= EMPACK_TYPE_NEG_FIXINT) && (object->type <= EMPACK_NEG_FIXINT_MAX)) { object->val.int8 = (int8_t)object->type; @@ -462,6 +470,229 @@ empack_parse_one_return: return ret; } +uint32_t +empack_print(empack_t *empack, + void (*print_func)(const char *buf, uint32_t buflen, void *arg), + void *arg, + const char *indent) +{ + uint32_t ret = 0; + int indent_depth = 0; + size_t indent_len = indent ? strlen(indent) : 0; + empack_obj_t *cur = &empack->root; + +#define EMPACK_PRINT(buf, len) do { \ + const char *__buf = (buf); \ + uint32_t __len = (len); \ + ret += __len; \ + print_func(__buf, len, arg); \ +} while(0) + +#define EMPACK_PRINT_INDENT() do { \ + if (indent) { \ + for (int __i = 0; __i < indent_depth; __i++) { \ + EMPACK_PRINT(indent, indent_len); \ + } \ + } \ +} while (0) + +#define EMPACK_PRINT_NEWLINE() do { \ + if (indent) { \ + EMPACK_PRINT("\r\n", 2); \ + } \ +} while (0) + + while (cur) { + /* Large enough for uint64/double */ + char tmp[24]; + int tmp_len; + + /* Print indent for everything except map value */ + if (cur->parent && EMPACK_IS_MAP(cur->parent->type)) { + empack_map_t *pmap = &cur->parent->val.map; + + if (cur == &pmap->pairs[pmap->pairs_parsed].key) { + EMPACK_PRINT_INDENT(); + } + } else { + EMPACK_PRINT_INDENT(); + } + + switch (cur->type) { + case EMPACK_TYPE_FIXMAP: + case EMPACK_TYPE_MAP16: + case EMPACK_TYPE_MAP32: + EMPACK_PRINT("{", 1); + EMPACK_PRINT_NEWLINE(); + cur->val.map.pairs_parsed = 0; + indent_depth++; + break; + + case EMPACK_TYPE_FIXARRAY: + case EMPACK_TYPE_ARRAY16: + case EMPACK_TYPE_ARRAY32: + EMPACK_PRINT("[", 1); + EMPACK_PRINT_NEWLINE(); + cur->val.array.objects_parsed = 0; + indent_depth++; + break; + + case EMPACK_TYPE_FIXSTR: + case EMPACK_TYPE_STR8: + case EMPACK_TYPE_STR16: + case EMPACK_TYPE_STR32: + EMPACK_PRINT("\"", 1); + EMPACK_PRINT(cur->val.str.data, cur->val.str.size); + EMPACK_PRINT("\"", 1); + break; + + case EMPACK_TYPE_BIN8: + case EMPACK_TYPE_BIN16: + case EMPACK_TYPE_BIN32: + EMPACK_PRINT("\"", 1); + for (uint32_t i = 0; i < cur->val.bin.size; i++) { + tmp_len = snprintf(tmp, sizeof(tmp), "%02X", ((uint8_t *)cur->val.bin.data)[i]); + EMPACK_PRINT(tmp, tmp_len); + } + EMPACK_PRINT("\"", 1); + break; + + case EMPACK_TYPE_EXT8: + case EMPACK_TYPE_EXT16: + case EMPACK_TYPE_EXT32: + case EMPACK_TYPE_FIXEXT1: + case EMPACK_TYPE_FIXEXT2: + case EMPACK_TYPE_FIXEXT4: + case EMPACK_TYPE_FIXEXT8: + case EMPACK_TYPE_FIXEXT16: + EMPACK_PRINT("\"", 1); + for (uint32_t i = 0; i < cur->val.ext.size; i++) { + tmp_len = snprintf(tmp, sizeof(tmp), "%02X", ((uint8_t *)cur->val.ext.data)[i]); + EMPACK_PRINT(tmp, tmp_len); + } + EMPACK_PRINT("\"", 1); + break; + + case EMPACK_TYPE_NIL: + EMPACK_PRINT("null", 4); + break; + case EMPACK_TYPE_ERROR: + EMPACK_PRINT("ERROR", 5); + break; + case EMPACK_TYPE_FALSE: + EMPACK_PRINT("false", 5); + break; + case EMPACK_TYPE_TRUE: + EMPACK_PRINT("true", 4); + break; + case EMPACK_TYPE_FLOAT32: + tmp_len = snprintf(tmp, sizeof(tmp), "%f", cur->val.float32); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_FLOAT64: + tmp_len = snprintf(tmp, sizeof(tmp), "%f", cur->val.float64); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_POS_FIXINT: + case EMPACK_TYPE_UINT8: + tmp_len = snprintf(tmp, sizeof(tmp), "%u", cur->val.uint8); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_UINT16: + tmp_len = snprintf(tmp, sizeof(tmp), "%u", cur->val.uint16); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_UINT32: + tmp_len = snprintf(tmp, sizeof(tmp), "%u", cur->val.uint32); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_UINT64: + tmp_len = snprintf(tmp, sizeof(tmp), "%u%u", + (uint32_t)(cur->val.uint64 >> 32), + (uint32_t)cur->val.uint64); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_NEG_FIXINT: + case EMPACK_TYPE_INT8: + tmp_len = snprintf(tmp, sizeof(tmp), "%d", cur->val.int8); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_INT16: + tmp_len = snprintf(tmp, sizeof(tmp), "%d", cur->val.int16); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_INT32: + tmp_len = snprintf(tmp, sizeof(tmp), "%d", cur->val.int32); + EMPACK_PRINT(tmp, tmp_len); + break; + case EMPACK_TYPE_INT64: + { + uint64_t tmp_num; + const char *sign = ""; + + if (cur->val.int64 < 0) { + tmp_num = -cur->val.int64; + sign = "-"; + } else { + tmp_num = cur->val.int64; + } + + tmp_len = snprintf(tmp, sizeof(tmp), "%s%u%u", + sign, + (uint32_t)(tmp_num >> 32), + (uint32_t)tmp_num); + EMPACK_PRINT(tmp, tmp_len); + } + break; + default: + return (0); + } + + if (cur->parent && EMPACK_IS_MAP(cur->parent->type)) { + empack_map_t *pmap = &cur->parent->val.map; + + if (cur == &pmap->pairs[pmap->pairs_parsed].key) { + EMPACK_PRINT(":", 1); + if (indent) { + EMPACK_PRINT(" ", 1); + } + } else { + if (pmap->pairs_parsed != (pmap->pair_count - 1)) { + EMPACK_PRINT(",", 1); + } + EMPACK_PRINT_NEWLINE(); + + pmap->pairs_parsed++; + if (pmap->pairs_parsed == pmap->pair_count) { + indent_depth--; + EMPACK_PRINT_INDENT(); + EMPACK_PRINT("}", 1); + EMPACK_PRINT_NEWLINE(); + } + } + } else if (cur->parent && EMPACK_IS_ARRAY(cur->parent->type)) { + empack_array_t *parr = &cur->parent->val.array; + + if (parr->objects_parsed != (parr->object_count - 1)) { + EMPACK_PRINT(",", 1); + } + EMPACK_PRINT_NEWLINE(); + + parr->objects_parsed++; + if (parr->objects_parsed == parr->object_count) { + indent_depth--; + EMPACK_PRINT_INDENT(); + EMPACK_PRINT("]", 1); + EMPACK_PRINT_NEWLINE(); + } + } + + cur = cur->next; + } + + return (ret); +} + static void * empack_allocate(empack_t *empack, uint32_t size) { diff --git a/src/empack.h b/src/empack.h index b753459..d39db24 100644 --- a/src/empack.h +++ b/src/empack.h @@ -71,8 +71,8 @@ struct empack_map_struct { empack_map_pair_t *pairs; /** Number of key-value pairs expected to be in the map */ uint32_t pair_count; - /** Number of key-value pairs actually in the map */ - uint32_t pair_count_real; + /** Parsed count utilized in tree traversal */ + uint32_t pairs_parsed; }; struct empack_array_struct { @@ -80,8 +80,8 @@ struct empack_array_struct { empack_obj_t *objects; /** Number of objects expected to be in the array */ uint32_t object_count; - /** Number of objects actually in the array */ - uint32_t object_count_real; + /** Parsed count utilized in tree traversal */ + uint32_t objects_parsed; }; struct empack_str_struct { @@ -137,6 +137,11 @@ struct empack_obj_struct { * parsing. NULL if no parent is present. */ empack_obj_t *parent; + /** + * Object that comes immediately after this one in the original Msgpack + * buffer, NULL if last object + */ + empack_obj_t *next; }; struct empack_map_pair_struct { @@ -173,6 +178,16 @@ struct empack_struct { uint32_t arena_used; }; +/** Macro for whether the given type is a map */ +#define EMPACK_IS_MAP(type) ((type == EMPACK_TYPE_FIXMAP) || \ + (type == EMPACK_TYPE_MAP16) || \ + (type == EMPACK_TYPE_MAP32)) + +/** Macro for whether the given type is an array */ +#define EMPACK_IS_ARRAY(type) ((type == EMPACK_TYPE_FIXARRAY) || \ + (type == EMPACK_TYPE_ARRAY16) || \ + (type == EMPACK_TYPE_ARRAY16)) + /** * Initialize an empack struct, allowing it to be used for parsing. Calling this * on a previously initialzed empack struct will effectively reset it. @@ -213,4 +228,12 @@ empack_status_e empack_parse(empack_t *empack, const void *msgpack, uint32_t len */ empack_status_e empack_parse_one(empack_obj_t *object, const void *msgpack, uint32_t *len); +/** + * @returns Number of characters printed excluding the NULL terminator + */ +uint32_t empack_print(empack_t *empack, + void (*print_func)(const char *buf, uint32_t buflen, void *arg), + void *arg, + const char *indent); + #endif /* _EMPACK_H_ */