| #ifndef CN_PRINT_C |
| #define CN_PRINT_C |
| #define CN_INCLUDE_DUMPER |
| #ifdef CN_INCLUDE_DUMPER |
| #define _CRT_SECURE_NO_WARNINGS 1 |
| |
| #include <stdio.h> |
| #include <stdio.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| #ifdef EMACS_INDENTATION_HELPER |
| } /* Duh. */ |
| #endif |
| |
| #include <stdio.h> |
| #ifdef MSV_CRT |
| #include <winsock2.h> |
| #else |
| #define _snprintf snprintf |
| #endif |
| #include <string.h> |
| #include <stdbool.h> |
| #include <assert.h> |
| |
| #include "cn-cbor/cn-cbor.h" |
| #include "cbor.h" |
| |
| typedef struct _write_state { |
| char *rgbOutput; |
| size_t ib; |
| size_t cbLeft; |
| uint8_t *rgFlags; |
| const char *szIndentWith; |
| const char *szEndOfLine; |
| } cn_write_state; |
| |
| typedef bool (*cn_visit_func)(const cn_cbor *cb, int depth, void *context); |
| extern bool _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'}; |
| |
| bool _isWritable(cn_write_state *ws, size_t cb) |
| { |
| if (ws->rgbOutput == NULL) { |
| return true; |
| } |
| if (ws->ib + cb > ws->cbLeft) { |
| ws->ib = -1; |
| return false; |
| } |
| return true; |
| } |
| |
| bool 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; |
| return true; |
| } |
| return false; |
| } |
| |
| bool _doIndent(cn_write_state *ws, int depth) |
| { |
| int i; |
| char *sz = ws->rgbOutput + ws->ib; |
| const size_t cbIndentWith = strlen(ws->szIndentWith); |
| const size_t cbIndent = depth * cbIndentWith; |
| |
| if (ws->rgbOutput == NULL) { |
| ws->ib += cbIndent; |
| return true; |
| } |
| |
| if (!_isWritable(ws, cbIndent)) { |
| return false; |
| } |
| for (i = 0; i < depth; i++) { |
| memcpy(sz, ws->szIndentWith, cbIndentWith); |
| sz += cbIndentWith; |
| } |
| |
| ws->ib += cbIndent; |
| |
| return true; |
| } |
| |
| bool _print_encoder(const cn_cbor *cb, int depth, void *context) |
| { |
| size_t i; |
| char rgchT[256]; |
| int cch; |
| cn_write_state *ws = (cn_write_state *)context; |
| uint8_t 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_BYTES_CHUNKED: |
| case CN_CBOR_TEXT_CHUNKED: |
| break; |
| |
| 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", (unsigned int)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", (unsigned int)cb->v.sint); |
| write_data(ws, rgchT, cch); |
| break; |
| |
| #ifndef CBOR_NO_FLOAT |
| case CN_CBOR_FLOAT: |
| cch = _snprintf(rgchT, sizeof(rgchT), "%f", cb->v.f); |
| 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; |
| #endif |
| |
| 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; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool _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) { |
| if (!write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine))) { |
| return false; |
| } |
| if (!_doIndent(ws, depth)) { |
| return false; |
| } |
| } |
| |
| 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; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| ssize_t cn_cbor_printer_write(char *rgbBuffer, |
| size_t cbBuffer, |
| const cn_cbor *cb, |
| const char *szIndentWith, |
| const char *szEndOfLine) |
| { |
| uint8_t flags[128] = {0}; |
| char rgchZero[1] = {0}; |
| |
| cn_write_state ws = {rgbBuffer, 0, cbBuffer, flags, szIndentWith, szEndOfLine}; |
| if (!_visit(cb, _print_encoder, _print_breaker, &ws)) { |
| return -1; |
| } |
| if (!write_data(&ws, rgchZero, 1)) { |
| return -1; |
| } |
| |
| return (ssize_t)ws.ib; |
| } |
| |
| #ifdef EMACS_INDENTATION_HELPER |
| { /* Duh. */ |
| #endif |
| #ifdef _cplusplus |
| } /* extern "C" */ |
| #endif |
| |
| #endif // CN_INCLUDE_DUMPER |
| #endif // CN_PRINT_C |