#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 <arpa/inet.h> // needed for ntohl (e.g.) on Linux #include "cn-cbor/cn-cbor.h" #include "cbor.h" #define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0) void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) { cn_cbor* p = cb; assert(!p || !p->parent); 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_CONTEXT(p); p = p1; } } #ifndef CBOR_NO_FLOAT 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; } #endif /* CBOR_NO_FLOAT */ /* 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 > (size_t)(ebuf - pos)) \ CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \ stmt; \ pos += n; static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) { unsigned char *pos = pb->buf; unsigned char *ebuf = pb->ebuf; cn_cbor* parent = top_parent; int ib; unsigned int mt; int ai; uint64_t val; cn_cbor* cb = NULL; #ifndef CBOR_NO_FLOAT union { float f; uint32_t u; } u32; union { double d; uint64_t u; } u64; #endif /* CBOR_NO_FLOAT */ again: TAKE(pos, ebuf, 1, 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; } mt = ib >> 5; ai = ib & 0x1f; val = ai; cb = CN_CALLOC_CONTEXT(); 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++; 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_FALSE: cb->type = CN_CBOR_FALSE; break; case VAL_TRUE: cb->type = CN_CBOR_TRUE; break; case VAL_NIL: cb->type = CN_CBOR_NULL; break; case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break; case AI_2: #ifndef CBOR_NO_FLOAT cb->type = CN_CBOR_DOUBLE; cb->v.dbl = decode_half(val); #else /* CBOR_NO_FLOAT */ CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); #endif /* CBOR_NO_FLOAT */ break; case AI_4: #ifndef CBOR_NO_FLOAT cb->type = CN_CBOR_DOUBLE; u32.u = val; cb->v.dbl = u32.f; #else /* CBOR_NO_FLOAT */ CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); #endif /* CBOR_NO_FLOAT */ break; case AI_8: #ifndef CBOR_NO_FLOAT cb->type = CN_CBOR_DOUBLE; u64.u = val; cb->v.dbl = u64.d; #else /* CBOR_NO_FLOAT */ CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); #endif /* CBOR_NO_FLOAT */ 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; } cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) { cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL}; struct parse_buf pb; cn_cbor* ret; pb.buf = (unsigned char *)buf; pb.ebuf = (unsigned char *)buf+len; pb.err = CN_CBOR_NO_ERROR; ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher); if (ret != NULL) { /* mark as top node */ ret->parent = NULL; } else { if (catcher.first_child) { catcher.first_child->parent = 0; cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM); } //fail: if (errp) { errp->err = pb.err; errp->pos = pb.buf - (unsigned char *)buf; } return NULL; } return ret; } #ifdef __cplusplus } #endif #endif /* CN_CBOR_C */