Pretty Print First Pass
First pass at having a Pretty print function in order to get diagnostic
information.
The API needs to be reviewed
The output needs to be reviewed to match the specification as I have not
done this yet.
diff --git a/src/cn-print.c b/src/cn-print.c
new file mode 100644
index 0000000..81f4e14
--- /dev/null
+++ b/src/cn-print.c
@@ -0,0 +1,239 @@
+#ifndef CN_PRINT_C
+#define CN_PRINT_C
+#ifdef CN_INCLUDE_DUMPER
+#define _CRT_SECURE_NO_WARNINGS 1
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+#include <stdio.h>
+#include <winsock2.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "cn-cbor/cn-cbor.h"
+#include "cbor.h"
+
+typedef struct _write_state
+{
+ char * rgbOutput;
+ ssize_t ib;
+ size_t cbLeft;
+ byte * rgFlags;
+ const char * szIndentWith;
+ const char * szEndOfLine;
+} cn_write_state;
+
+typedef void(*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
+extern void _visit(const cn_cbor *cb,
+ cn_visit_func visitor,
+ cn_visit_func breaker,
+ void *context);
+
+const char RgchHex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+inline bool _isWritable(cn_write_state * ws, size_t cb)
+{
+ if (ws->rgbOutput == NULL) return true;
+ if ((ws->ib < 0) || (ws->ib + cb > ws->cbLeft)) {
+ ws->ib = -1;
+ return false;
+ }
+ return true;
+}
+
+inline void write_data(cn_write_state * ws, const char * sz, size_t cb)
+{
+ if (_isWritable(ws, cb)) {
+ if (ws->rgbOutput != NULL) memcpy(ws->rgbOutput + ws->ib, sz, cb);
+ ws->ib += cb;
+ }
+}
+
+inline void _doIndent(cn_write_state * ws, int depth)
+{
+ int i;
+ char * sz = ws->rgbOutput + ws->ib;
+ size_t cbIndentWith = strlen(ws->szIndentWith);
+ int cbIndent = depth * cbIndentWith;
+
+
+ if (ws->rgbOutput == NULL) {
+ ws->ib += cbIndent;
+ return;
+ }
+
+ if (_isWritable(ws, cbIndent)) {
+ for (i = 0; i < depth; i++) {
+ memcpy(sz, ws->szIndentWith, cbIndentWith);
+ sz += cbIndentWith;
+ }
+ }
+
+ ws->ib += cbIndent;
+
+ return;
+}
+
+void _print_encoder(const cn_cbor * cb, int depth, void * context)
+{
+ int i;
+ char rgchT[256];
+ int cch;
+ cn_write_state * ws = (cn_write_state *)context;
+ byte flags = ws->rgFlags[depth];
+
+ if (flags & 1) {
+ write_data(ws, ", ", 2);
+ ws->rgFlags[depth] &= 0xfe;
+
+ if (ws->szIndentWith) {
+ write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+ _doIndent(ws, depth);
+ }
+ }
+
+ if (flags & 2) {
+ write_data(ws, ": ", 2);
+ ws->rgFlags[depth] &= 0xfd;
+ }
+
+ switch (cb->type) {
+ case CN_CBOR_ARRAY:
+ write_data(ws, "[", 1);
+ ws->rgFlags[depth] |= 4;
+
+ if (ws->szIndentWith) {
+ write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+ _doIndent(ws, depth + 1);
+ }
+ break;
+
+ case CN_CBOR_MAP:
+ write_data(ws, "{", 1);
+ ws->rgFlags[depth] |= 8;
+
+ if (ws->szIndentWith) {
+ write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+ _doIndent(ws, depth + 1);
+ }
+ break;
+
+ case CN_CBOR_TAG:
+ case CN_CBOR_UINT:
+ case CN_CBOR_SIMPLE:
+ cch = _snprintf(rgchT, sizeof(rgchT), "%u", cb->v.uint);
+ write_data(ws, rgchT, cch);
+ break;
+
+ case CN_CBOR_FALSE:
+ write_data(ws, "false", 5);
+ break;
+
+ case CN_CBOR_TRUE:
+ write_data(ws, "true", 4);
+ break;
+
+ case CN_CBOR_NULL:
+ write_data(ws, "null", 4);
+ break;
+
+ case CN_CBOR_UNDEF:
+ write_data(ws, "undef", 5);
+ break;
+
+ case CN_CBOR_INT:
+ cch = _snprintf(rgchT, sizeof(rgchT), "%d", cb->v.sint);
+ write_data(ws, rgchT, cch);
+ break;
+
+ case CN_CBOR_DOUBLE:
+ cch = _snprintf(rgchT, sizeof(rgchT), "%f", cb->v.dbl);
+ write_data(ws, rgchT, cch);
+ break;
+
+ case CN_CBOR_INVALID:
+ write_data(ws, "invalid", 7);
+ break;
+
+ case CN_CBOR_TEXT:
+ write_data(ws, "\"", 1);
+ write_data(ws, cb->v.str, cb->length);
+ write_data(ws, "\"", 1);
+ break;
+
+ case CN_CBOR_BYTES:
+ write_data(ws, "h'", 2);
+ for (i = 0; i < cb->length; i++) {
+ write_data(ws, &RgchHex[(cb->v.str[i] / 16) & 0xf], 1);
+ write_data(ws, &RgchHex[cb->v.str[i] & 0xf], 1);
+ }
+ write_data(ws, "\'", 1);
+ break;
+ }
+
+ if (depth > 0) {
+ if (ws->rgFlags[depth - 1] & 4) ws->rgFlags[depth] |= 1;
+ else if (ws->rgFlags[depth - 1] & 8) {
+ if (flags & 2) ws->rgFlags[depth] |= 1;
+ else ws->rgFlags[depth] |= 2;
+ }
+ }
+}
+
+void _print_breaker(const cn_cbor * cb, int depth, void * context)
+{
+ cn_write_state * ws = (cn_write_state *)context;
+
+ switch (cb->type) {
+ case CN_CBOR_ARRAY:
+ if (ws->szIndentWith) {
+ write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+ _doIndent(ws, depth);
+ }
+
+ write_data(ws, "]", 1);
+ ws->rgFlags[depth + 1] = 0;
+ break;
+
+ case CN_CBOR_MAP:
+ if (ws->szIndentWith) {
+ write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+ _doIndent(ws, depth);
+ }
+
+ write_data(ws, "}", 1);
+ ws->rgFlags[depth + 1] = 0;
+ break;
+ }
+}
+
+ssize_t cn_cbor_printer_write(char * rgbBuffer, size_t cbBuffer, const cn_cbor * cb, const char * szIndentWith, const char * szEndOfLine)
+{
+ byte flags[128] = { 0 };
+ char rgchZero[1] = { 0 };
+
+ cn_write_state ws = { rgbBuffer, 0, cbBuffer, flags, szIndentWith, szEndOfLine };
+ _visit(cb, _print_encoder, _print_breaker, &ws);
+ write_data(&ws, rgchZero, 1);
+
+ return ws.ib;
+}
+
+#ifdef EMACS_INDENTATION_HELPER
+{ /* Duh. */
+#endif
+#ifdef _cplusplus
+} /* extern "C" */
+#endif
+
+#endif // CN_INCLUDE_DUMPER
+#endif // CN_PRINT_C