blob: 077b0a479dc70ac34840389fc637a06b3f15819a [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;
ssize_t ib;
size_t cbLeft;
uint8_t * 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' };
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;
}
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;
}
}
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;
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;
}
}
}
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;
default:
break;
}
}
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 };
_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