Initial commit, finally
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bc17916
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+all: size test
+ env MallocStackLogging=true ./test >new.out
+ -diff new.out expected.out
+
+test: test.c cbor.h cn-cbor.h cn-cbor.c
+ clang cn-cbor.c test.c -o test
+
+size: cn-cbor.o
+ size cn-cbor.o
+ size -m cn-cbor.o
+
+cn-cbor.o: cn-cbor.c cn-cbor.h cbor.h
+ clang -I /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/include -arch armv7 -Os -c cn-cbor.c
+
+cn-cbor-play.zip: Makefile cbor.h cn-cbor.c cn-cbor.h expected.out test.c
+ zip $@ $^
diff --git a/README.md b/README.md
index a1e9a8f..9bf6816 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,25 @@
-# cn-cbor
-A constrained node implementation of CBOR in C
+# cn-cbor: A constrained node implementation of CBOR in C
+
+This is a constrained node implementation of [CBOR](http://cbor.io) in
+C that I threw together in 2013, before the publication of
+[RFC 7049](http://tools.ietf.org/html/rfc7049), to validate certain
+implementability considerations.
+
+Its API model was inspired by
+[nxjson](https://bitbucket.org/yarosla/nxjson). It turns out that
+this API model actually works even better with the advantages of the
+CBOR format.
+
+This code has been used in a number of research implementations on
+constrained nodes, with resulting code sizes appreciably under 1 KiB
+on ARM platforms.
+
+I always meant to improve the interface some more with certain API
+changes, in order to get even closer to 0.5 KiB, but I ran out of
+time. So here it is. If I do get around to making these changes, the
+API will indeed change a bit, so please be forewarned.
+
+(To run the test, please add
+[cases.cbor](https://github.com/cabo/cbor-ruby/blob/master/spec/cases.cbor).)
+
+License: MIT
diff --git a/cbor.h b/cbor.h
new file mode 100644
index 0000000..547ad65
--- /dev/null
+++ b/cbor.h
@@ -0,0 +1,69 @@
+#ifndef CBOR_PROTOCOL_H__
+#define CBOR_PROTOCOL_H__
+
+/* The 8 major types */
+#define MT_UNSIGNED 0
+#define MT_NEGATIVE 1
+#define MT_BYTES 2
+#define MT_TEXT 3
+#define MT_ARRAY 4
+#define MT_MAP 5
+#define MT_TAG 6
+#define MT_PRIM 7
+
+/* The initial bytes resulting from those */
+#define IB_UNSIGNED (MT_UNSIGNED << 5)
+#define IB_NEGATIVE (MT_NEGATIVE << 5)
+#define IB_BYTES (MT_BYTES << 5)
+#define IB_TEXT (MT_TEXT << 5)
+#define IB_ARRAY (MT_ARRAY << 5)
+#define IB_MAP (MT_MAP << 5)
+#define IB_TAG (MT_TAG << 5)
+#define IB_PRIM (MT_PRIM << 5)
+
+#define IB_NEGFLAG (IB_NEGATIVE - IB_UNSIGNED)
+#define IB_NEGFLAG_AS_BIT(ib) ((ib) >> 5)
+#define IB_TEXTFLAG (IB_TEXT - IB_BYTES)
+
+#define IB_AI(ib) ((ib) & 0x1F)
+#define IB_MT(ib) ((ib) >> 5)
+
+/* Tag numbers handled by this implementation */
+#define TAG_TIME_EPOCH 1
+#define TAG_BIGNUM 2
+#define TAG_BIGNUM_NEG 3
+#define TAG_URI 32
+#define TAG_RE 35
+
+/* Initial bytes of those tag numbers */
+#define IB_TIME_EPOCH (IB_TAG + TAG_TIME_EPOCH)
+#define IB_BIGNUM (IB_TAG + TAG_BIGNUM)
+#define IB_BIGNUM_NEG (IB_TAG + TAG_BIGNUM_NEG)
+/* TAG_URI and TAG_RE are non-immediate tags */
+
+/* Simple values handled by this implementation */
+#define VAL_NIL 22
+#define VAL_FALSE 20
+#define VAL_TRUE 21
+
+/* Initial bytes of those simple values */
+#define IB_NIL (IB_PRIM + VAL_NIL)
+#define IB_FALSE (IB_PRIM + VAL_FALSE)
+#define IB_TRUE (IB_PRIM + VAL_TRUE)
+
+/* AI values with more data in head */
+#define AI_1 24
+#define AI_2 25
+#define AI_4 26
+#define AI_8 27
+#define AI_INDEF 31
+#define IB_BREAK (IB_PRIM + AI_INDEF)
+/* For */
+#define IB_UNUSED (IB_TAG + AI_INDEF)
+
+/* Floating point initial bytes */
+#define IB_FLOAT2 (IB_PRIM + AI_2)
+#define IB_FLOAT4 (IB_PRIM + AI_4)
+#define IB_FLOAT8 (IB_PRIM + AI_8)
+
+#endif
diff --git a/cn-cbor.c b/cn-cbor.c
new file mode 100644
index 0000000..9e3e814
--- /dev/null
+++ b/cn-cbor.c
@@ -0,0 +1,247 @@
+#ifndef CN_CBOR_C
+#define CN_CBOR_C
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "cn-cbor.h"
+
+// can be redefined, e.g. for pool allocation
+#ifndef CN_CBOR_CALLOC
+#define CN_CBOR_CALLOC() calloc(1, sizeof(cn_cbor))
+#define CN_CBOR_FREE(cb) free((void*)(cb))
+#endif
+
+#define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0)
+
+void cn_cbor_free(const cn_cbor* cb) {
+ cn_cbor* p = (cn_cbor*) cb;
+ while (p) {
+ cn_cbor* p1;
+ while ((p1 = p->first_child)) { /* go down */
+ p = p1;
+ }
+ if (!(p1 = p->next)) { /* go up next */
+ if ((p1 = p->parent))
+ p1->first_child = 0;
+ }
+ CN_CBOR_FREE(p);
+ p = p1;
+ }
+}
+
+static double decode_half(int half) {
+ int exp = (half >> 10) & 0x1f;
+ int mant = half & 0x3ff;
+ double val;
+ if (exp == 0) val = ldexp(mant, -24);
+ else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
+ else val = mant == 0 ? INFINITY : NAN;
+ return half & 0x8000 ? -val : val;
+}
+
+/* Fix these if you can't do non-aligned reads */
+#define ntoh8p(p) (*(unsigned char*)(p))
+#define ntoh16p(p) (ntohs(*(unsigned short*)(p)))
+#define ntoh32p(p) (ntohl(*(unsigned long*)(p)))
+static uint64_t ntoh64p(unsigned char *p) {
+ uint64_t ret = ntoh32p(p);
+ ret <<= 32;
+ ret += ntoh32p(p+4);
+ return ret;
+}
+
+static cn_cbor_type mt_trans[] = {
+ CN_CBOR_UINT, CN_CBOR_INT,
+ CN_CBOR_BYTES, CN_CBOR_TEXT,
+ CN_CBOR_ARRAY, CN_CBOR_MAP,
+ CN_CBOR_TAG, CN_CBOR_SIMPLE,
+};
+
+struct parse_buf {
+ unsigned char *buf;
+ unsigned char *ebuf;
+ cn_cbor_error err;
+};
+
+#define TAKE(pos, ebuf, n, stmt) \
+ if (n > (ebuf - pos)) \
+ CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \
+ stmt; \
+ pos += n;
+
+static cn_cbor *decode_item (struct parse_buf *pb, cn_cbor* top_parent) {
+ unsigned char *pos = pb->buf;
+ unsigned char *ebuf = pb->ebuf;
+ cn_cbor* parent = top_parent;
+again:
+ TAKE(pos, ebuf, 1, int ib = ntoh8p(pos) );
+ if (ib == IB_BREAK) {
+ if (!(parent->flags & CN_CBOR_FL_INDEF))
+ CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF);
+ switch (parent->type) {
+ case CN_CBOR_BYTES: case CN_CBOR_TEXT:
+ parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */
+ break;
+ case CN_CBOR_MAP:
+ if (parent->length & 1)
+ CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP);
+ default:;
+ }
+ goto complete;
+ }
+ unsigned int mt = ib >> 5;
+ int ai = ib & 0x1f;
+ uint64_t val = ai;
+
+ cn_cbor* cb = CN_CBOR_CALLOC();
+ if (!cb)
+ CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY);
+
+ cb->type = mt_trans[mt];
+
+ cb->parent = parent;
+ if (parent->last_child) {
+ parent->last_child->next = cb;
+ } else {
+ parent->first_child = cb;
+ }
+ parent->last_child = cb;
+ parent->length++;
+
+ cn_cbor *it;
+ cn_cbor *it2;
+ uint64_t i;
+
+ switch (ai) {
+ case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos)) ; break;
+ case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; break;
+ case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; break;
+ case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; break;
+ case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI);
+ case AI_INDEF:
+ if ((mt - MT_BYTES) <= MT_MAP) {
+ cb->flags |= CN_CBOR_FL_INDEF;
+ goto push;
+ } else {
+ CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF);
+ }
+ }
+ // process content
+ switch (mt) {
+ case MT_UNSIGNED:
+ cb->v.uint = val; /* to do: Overflow check */
+ break;
+ case MT_NEGATIVE:
+ cb->v.sint = ~val; /* to do: Overflow check */
+ break;
+ case MT_BYTES: case MT_TEXT:
+ cb->v.str = (char *) pos;
+ cb->length = val;
+ TAKE(pos, ebuf, val, );
+ break;
+ case MT_MAP:
+ val <<= 1;
+ /* fall through */
+ case MT_ARRAY:
+ if ((cb->v.count = val)) {
+ cb->flags |= CN_CBOR_FL_COUNT;
+ goto push;
+ }
+ break;
+ case MT_TAG:
+ cb->v.uint = val;
+ goto push;
+ case MT_PRIM:
+ switch (ai) {
+ case VAL_NIL: cb->type = CN_CBOR_NULL; break;
+ case VAL_FALSE: cb->type = CN_CBOR_FALSE; break;
+ case VAL_TRUE: cb->type = CN_CBOR_TRUE; break;
+ case AI_2: cb->type = CN_CBOR_DOUBLE; cb->v.dbl = decode_half(val); break;
+ case AI_4:
+ cb->type = CN_CBOR_DOUBLE;
+ union {
+ float f;
+ uint32_t u;
+ } u32;
+ u32.u = val;
+ cb->v.dbl = u32.f;
+ break;
+ case AI_8:
+ cb->type = CN_CBOR_DOUBLE;
+ union {
+ double d;
+ uint64_t u;
+ } u64;
+ u64.u = val;
+ cb->v.dbl = u64.d;
+ break;
+ default: cb->v.uint = val;
+ }
+ }
+fill: /* emulate loops */
+ if (parent->flags & CN_CBOR_FL_INDEF) {
+ if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT)
+ if (cb->type != parent->type)
+ CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING);
+ goto again;
+ }
+ if (parent->flags & CN_CBOR_FL_COUNT) {
+ if (--parent->v.count)
+ goto again;
+ }
+ /* so we are done filling parent. */
+complete: /* emulate return from call */
+ if (parent == top_parent) {
+ if (pos != ebuf) /* XXX do this outside */
+ CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED);
+ pb->buf = pos;
+ return cb;
+ }
+ cb = parent;
+ parent = parent->parent;
+ goto fill;
+push: /* emulate recursive call */
+ parent = cb;
+ goto again;
+fail:
+ pb->buf = pos;
+ return 0;
+}
+
+const cn_cbor* cn_cbor_decode(const char* buf, size_t len, cn_cbor_errback *errp) {
+ cn_cbor catcher = {CN_CBOR_INVALID};
+ struct parse_buf pb = {(unsigned char *)buf, (unsigned char *)buf+len};
+ cn_cbor* ret = decode_item(&pb, &catcher);
+ if (ret) {
+ ret->parent = 0; /* mark as top node */
+ } else {
+ if (catcher.first_child) {
+ catcher.first_child->parent = 0;
+ cn_cbor_free(catcher.first_child);
+ }
+ fail:
+ if (errp) {
+ errp->err = pb.err;
+ errp->pos = pb.buf - (unsigned char *)buf;
+ }
+ return 0;
+ }
+ return ret;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CN_CBOR_C */
diff --git a/cn-cbor.h b/cn-cbor.h
new file mode 100644
index 0000000..62d8b78
--- /dev/null
+++ b/cn-cbor.h
@@ -0,0 +1,78 @@
+#ifndef CN_CBOR_H
+#define CN_CBOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+/* protocol constants: */
+#include "cbor.h"
+
+typedef enum cn_cbor_type {
+ CN_CBOR_NULL,
+ CN_CBOR_FALSE, CN_CBOR_TRUE,
+ CN_CBOR_UINT, CN_CBOR_INT,
+ CN_CBOR_BYTES, CN_CBOR_TEXT,
+ CN_CBOR_BYTES_CHUNKED, CN_CBOR_TEXT_CHUNKED, /* += 2 */
+ CN_CBOR_ARRAY, CN_CBOR_MAP,
+ CN_CBOR_TAG,
+ CN_CBOR_SIMPLE, CN_CBOR_DOUBLE,
+ CN_CBOR_INVALID
+} cn_cbor_type;
+
+typedef enum cn_cbor_flags {
+ CN_CBOR_FL_COUNT = 1,
+ CN_CBOR_FL_INDEF = 2,
+ CN_CBOR_FL_OWNER = 0x80, /* of str */
+} cn_cbor_flags;
+
+typedef struct cn_cbor {
+ cn_cbor_type type;
+ cn_cbor_flags flags;
+ union {
+ const char* str;
+ long sint;
+ unsigned long uint;
+ double dbl;
+ unsigned long count; /* for use during filling */
+ } v; /* TBD: optimize immediate */
+ int length;
+ struct cn_cbor* first_child;
+ struct cn_cbor* last_child;
+ struct cn_cbor* next;
+ struct cn_cbor* parent;
+} cn_cbor;
+
+typedef enum cn_cbor_error {
+ CN_CBOR_NO_ERROR,
+ CN_CBOR_ERR_OUT_OF_DATA,
+ CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED,
+ CN_CBOR_ERR_ODD_SIZE_INDEF_MAP,
+ CN_CBOR_ERR_BREAK_OUTSIDE_INDEF,
+ CN_CBOR_ERR_MT_UNDEF_FOR_INDEF,
+ CN_CBOR_ERR_RESERVED_AI,
+ CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING,
+ CN_CBOR_ERR_OUT_OF_MEMORY,
+} cn_cbor_error;
+
+typedef struct cn_cbor_errback {
+ int pos;
+ cn_cbor_error err;
+} cn_cbor_errback;
+
+const cn_cbor* cn_cbor_decode(const char* buf, size_t len, cn_cbor_errback *errp);
+const cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key);
+const cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key);
+const cn_cbor* cn_cbor_index(const cn_cbor* cb, int idx);
+
+const cn_cbor* cn_cbor_alloc(cn_cbor_type t);
+void cn_cbor_free(const cn_cbor* js);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CN_CBOR_H */
diff --git a/expected.out b/expected.out
new file mode 100644
index 0000000..3ed0d13
--- /dev/null
+++ b/expected.out
@@ -0,0 +1,277 @@
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+511
+[
+ 0
+ 1
+ 10
+ 23
+ 24
+ 25
+ 100
+ 1000
+ 1000000
+ 1000000000000
+ 18446744073709551615
+ 2(
+ h'010000000000000000'
+ )
+ OVERFLOW
+ 3(
+ h'010000000000000000'
+ )
+ -1
+ -10
+ -100
+ -1000
+ 0.000000e+00
+ -0.000000e+00
+ 1.000000e+00
+ 1.100000e+00
+ 1.500000e+00
+ 6.550400e+04
+ 1.000000e+05
+ 3.402823e+38
+ 1.000000e+300
+ 5.960464e-08
+ 6.103516e-05
+ -4.000000e+00
+ -4.100000e+00
+ inf
+ nan
+ -inf
+ inf
+ nan
+ -inf
+ inf
+ nan
+ -inf
+ false
+ true
+ null
+ simple(23)
+ simple(16)
+ simple(24)
+ simple(255)
+ 0(
+ "2013-03-21T20:04:00Z"
+ )
+ 1(
+ 1363896240
+ )
+ 1(
+ 1.363896e+09
+ )
+ 23(
+ h'01020304'
+ )
+ 24(
+ h'6449455446'
+ )
+ 32(
+ "http://www.example.com"
+ )
+ h''
+ h'01020304'
+ ""
+ "a"
+ "IETF"
+ ""\"
+ "ü"
+ "水"
+ "𐅑"
+ [
+ ]
+ [
+ 1
+ 2
+ 3
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ ]
+ {
+ }
+ {
+ 1
+ 2
+ 3
+ 4
+ }
+ {
+ "a"
+ 1
+ "b"
+ [
+ 2
+ 3
+ ]
+ }
+ [
+ "a"
+ {
+ "b"
+ "c"
+ }
+ ]
+ {
+ "a"
+ "A"
+ "b"
+ "B"
+ "c"
+ "C"
+ "d"
+ "D"
+ "e"
+ "E"
+ }
+ (_
+
+ h'0102'
+ h'030405'
+ )
+ (_
+ "strea"
+ "ming"
+ )
+ [
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ ]
+ {
+ "a"
+ 1
+ "b"
+ [
+ 2
+ 3
+ ]
+ }
+ [
+ "a"
+ {
+ "b"
+ "c"
+ }
+ ]
+ {
+ "Fun"
+ true
+ "Amt"
+ -2
+ }
+]
+
+CN_CBOR_ERR_BREAK_OUTSIDE_INDEF at 1
+CN_CBOR_ERR_MT_UNDEF_FOR_INDEF at 1
+CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED at 1
+CN_CBOR_ERR_OUT_OF_DATA at 1
+CN_CBOR_ERR_RESERVED_AI at 1
+CN_CBOR_ERR_ODD_SIZE_INDEF_MAP at 3
+CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING at 2
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..0d8acab
--- /dev/null
+++ b/test.c
@@ -0,0 +1,126 @@
+#include <unistd.h>
+#include "cn-cbor.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define ERROR(msg, p) fprintf(stderr, "ERROR: " msg " %s\n", (p));
+
+static char* load_file(const char* filepath, char **end) {
+ struct stat st;
+ if (stat(filepath, &st)==-1) {
+ // ERROR("can't find file", filepath);
+ return 0;
+ }
+ int fd=open(filepath, O_RDONLY);
+ if (fd==-1) {
+ ERROR("can't open file", filepath);
+ return 0;
+ }
+ char* text=malloc(st.st_size+1); // this is not going to be freed
+ if (st.st_size!=read(fd, text, st.st_size)) {
+ ERROR("can't read file", filepath);
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ text[st.st_size]='\0';
+ *end = text + st.st_size;
+ return text;
+}
+
+static void dump(const cn_cbor* cb, char* out, char** end, int indent) {
+ if (!cb)
+ goto done;
+ int i;
+ cn_cbor* cp;
+ char finchar = ')'; /* most likely */
+
+#define CPY(s, l) memcpy(out, s, l); out += l;
+#define OUT(s) CPY(s, sizeof(s)-1)
+#define PRF(f, a) out += sprintf(out, f, a)
+
+ for (i = 0; i < indent; i++) *out++ = ' ';
+ switch (cb->type) {
+ case CN_CBOR_TEXT_CHUNKED: OUT("(_\n"); goto sequence;
+ case CN_CBOR_BYTES_CHUNKED: OUT("(_\n\n"); goto sequence;
+ case CN_CBOR_TAG: PRF("%ld(\n", cb->v.sint); goto sequence;
+ case CN_CBOR_ARRAY: finchar = ']'; OUT("[\n"); goto sequence;
+ case CN_CBOR_MAP: finchar = '}'; OUT("{\n"); goto sequence;
+ sequence:
+ for (cp = cb->first_child; cp; cp = cp->next) {
+ dump(cp, out, &out, indent+2);
+ }
+ for (i=0; i<indent; i++) *out++ = ' ';
+ *out++ = finchar;
+ break;
+ case CN_CBOR_BYTES: OUT("h'");
+ for (i=0; i<cb->length; i++)
+ PRF("%02x", cb->v.str[i] & 0xff);
+ *out++ = '\'';
+ break;
+ case CN_CBOR_TEXT: *out++ = '"';
+ CPY(cb->v.str, cb->length); /* should escape stuff */
+ *out++ = '"';
+ break;
+ case CN_CBOR_NULL: OUT("null"); break;
+ case CN_CBOR_TRUE: OUT("true"); break;
+ case CN_CBOR_FALSE: OUT("false"); break;
+ case CN_CBOR_INT: PRF("%ld", cb->v.sint); break;
+ case CN_CBOR_UINT: PRF("%lu", cb->v.uint); break;
+ case CN_CBOR_DOUBLE: PRF("%e", cb->v.dbl); break;
+ case CN_CBOR_SIMPLE: PRF("simple(%ld)", cb->v.sint); break;
+ default: PRF("???%d???", cb->type); break;
+ }
+ *out++ = '\n';
+done:
+ *end = out;
+}
+
+
+char *err_name[] = {
+ "CN_CBOR_NO_ERROR",
+ "CN_CBOR_ERR_OUT_OF_DATA",
+ "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED",
+ "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP",
+ "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF",
+ "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF",
+ "CN_CBOR_ERR_RESERVED_AI",
+ "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
+ "CN_CBOR_ERR_OUT_OF_MEMORY",
+};
+
+void cn_cbor_decode_test(char *buf, int len) {
+ struct cn_cbor_errback back;
+ const cn_cbor *ret = cn_cbor_decode(buf, len, &back);
+ if (ret)
+ printf("oops 1");
+ printf("%s at %d\n", err_name[back.err], back.pos);
+}
+
+int main() {
+ char buf[100000];
+ char *end;
+ char *s = load_file("cases.cbor", &end);
+ printf("%zd\n", end-s);
+ const cn_cbor *cb = cn_cbor_decode(s, end-s, 0);
+ if (cb) {
+ dump(cb, buf, &end, 0);
+ *end = 0;
+ printf("%s\n", buf);
+ cn_cbor_free(cb);
+ cb = 0; /* for leaks testing */
+ }
+ cn_cbor_decode_test("\xff", 1); /* break outside indef */
+ cn_cbor_decode_test("\x1f", 1); /* mt undef for indef */
+ cn_cbor_decode_test("\x00\x00", 2); /* not all data consumed */
+ cn_cbor_decode_test("\x81", 1); /* out of data */
+ cn_cbor_decode_test("\x1c", 1); /* reserved ai */
+ cn_cbor_decode_test("\xbf\x00\xff", 3); /* odd size indef map */
+ cn_cbor_decode_test("\x7f\x40\xff", 3); /* wrong nesting in indef string */
+ system("leaks test");
+}
+
+/* cn-cbor.c:112: CN_CBOR_FAIL("out of memory"); */