| /* SDPX-License-Identifier: MIT */ |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| #ifdef EMACS_INDENTATION_HELPER |
| } /* Duh. */ |
| #endif |
| |
| #include "cn-cbor/cn-cbor.h" |
| #include "cbor.h" |
| #include <common.h> |
| |
| #define CBOR_NO_FLOAT 1 |
| |
| #define CN_CBOR_FAIL(code) \ |
| do { \ |
| pb->err = code; \ |
| goto fail; \ |
| } while (0) |
| |
| CN_CBOR_EXPORT |
| void cn_cbor_dont_free_data(cn_cbor *cbor) |
| { |
| cbor->flags |= CN_CBOR_FL_EXT_DATA; |
| } |
| |
| CN_CBOR_EXPORT |
| void cn_cbor_free(cn_cbor *cb CBOR_CONTEXT) |
| { |
| cn_cbor *p = (cn_cbor *)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; |
| } |
| } |
| |
| if ((p->flags & CN_CBOR_FL_EXT_DATA) == 0) { |
| switch (p->type) { |
| case CN_CBOR_BYTES: |
| case CN_CBOR_TEXT: |
| CN_CBOR_FREE_CONTEXT((void *) p->v.bytes); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| if ((p->flags & CN_CBOR_FL_EXT_SELF) == 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 */ |
| |
| #define ntoh8p(p) (*(unsigned char *)(p)) |
| |
| #ifndef CBOR_ALIGN_READS |
| #define ntoh16p(p) (ntohs(*(unsigned short *)(p))) |
| #define ntoh32p(p) (ntohl(*(unsigned long *)(p))) |
| #else |
| static uint16_t ntoh16p(unsigned char *p) |
| { |
| uint16_t tmp; |
| memcpy(&tmp, p, sizeof(tmp)); |
| return ntohs(tmp); |
| } |
| |
| static uint32_t ntoh32p(unsigned char *p) |
| { |
| uint32_t tmp; |
| memcpy(&tmp, p, sizeof(tmp)); |
| return ntohl(tmp); |
| } |
| #endif /* CBOR_ALIGN_READS */ |
| |
| 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 = 0; |
| unsigned int mt = 0; |
| int ai = 0; |
| uint64_t val = 0; |
| 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); |
| } |
| break; |
| |
| case CN_CBOR_ARRAY: |
| break; |
| |
| default: |
| CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); |
| } |
| 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); |
| } |
| default: |
| break; |
| } |
| |
| // 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 = (size_t)val; |
| cb->flags |= CN_CBOR_FL_EXT_DATA; |
| 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((int) 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 = (uint32_t)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; |
| if (24 <= val && val < 32) { |
| CN_CBOR_FAIL(CN_CBOR_ERR_INVALID_PARAMETER); |
| } |
| break; |
| } |
| break; |
| |
| default: |
| CN_CBOR_FAIL(CN_CBOR_ERR_INVALID_PARAMETER); |
| } |
| |
| 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_EXPORT |
| 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 = NULL; |
| |
| 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 |