blob: ce5fb9e9f33b8e4787358bca177e42e54d888142 [file] [log] [blame]
#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