Simple interface for Mbed Cloud Client

Dependents:  

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers cn-cbor.c Source File

cn-cbor.c

00001 #ifndef CN_CBOR_C
00002 #define CN_CBOR_C
00003 
00004 #ifdef  __cplusplus
00005 extern "C" {
00006 #endif
00007 #ifdef EMACS_INDENTATION_HELPER
00008 } /* Duh. */
00009 #endif
00010 
00011 #include <stdlib.h>
00012 #include <stdint.h>
00013 #include <string.h>
00014 #include <assert.h>
00015 #include <math.h>
00016 #ifdef CBOR_CAN_DO_UNALIGNED_READS
00017 #include <arpa/inet.h> // needed for ntohl (e.g.) on Linux
00018 #endif
00019 
00020 #include "cn-cbor.h"
00021 #include "cbor.h"
00022 
00023 
00024 #define CN_CBOR_FAIL(code) do { pb->err = code;  goto fail; } while(0)
00025 
00026 void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) {
00027   cn_cbor* p = cb;
00028   assert(!p || !p->parent);
00029   while (p) {
00030     cn_cbor* p1;
00031     while ((p1 = p->first_child)) { /* go down */
00032       p = p1;
00033     }
00034     if (!(p1 = p->next)) {   /* go up next */
00035       if ((p1 = p->parent))
00036         p1->first_child = 0;
00037     }
00038     CN_CBOR_FREE_CONTEXT(p);
00039     p = p1;
00040   }
00041 }
00042 
00043 #ifndef CBOR_NO_FLOAT
00044 static double decode_half(int half) {
00045   int exp = (half >> 10) & 0x1f;
00046   int mant = half & 0x3ff;
00047   double val = 0;
00048   if (exp == 0) {
00049     val = ldexp(mant, -24);
00050   } else if (exp != 31) {
00051     val = ldexp(mant + 1024, exp - 25);
00052   } else {
00053     if (mant == 0) {
00054       val = INFINITY;
00055     } else {
00056       val = NAN;
00057     }
00058   }
00059   return half & 0x8000 ? -val : val;
00060 }
00061 #endif /* CBOR_NO_FLOAT */
00062 
00063 /* Fix these if you can't do non-aligned reads */
00064 #define ntoh8p(p) (*(unsigned char*)(p))
00065 #ifdef CBOR_CAN_DO_UNALIGNED_READS
00066 #define ntoh16p(p) (ntohs(*(unsigned short*)(p)))
00067 #define ntoh32p(p) (ntohl(*(unsigned long*)(p)))
00068 #else
00069 static uint16_t ntoh16p(unsigned char *p) {
00070   uint16_t ret = ntoh8p(p);
00071   ret <<= 8;
00072   ret += ntoh8p(p+1);
00073   return ret;
00074 }
00075 static uint32_t ntoh32p(unsigned char *p) {
00076   uint64_t ret = ntoh16p(p);
00077   ret <<= 16;
00078   ret += ntoh16p(p+2);
00079   return ret;
00080 }
00081 #endif
00082 static uint64_t ntoh64p(unsigned char *p) {
00083   uint64_t ret = ntoh32p(p);
00084   ret <<= 32;
00085   ret += ntoh32p(p+4);
00086   return ret;
00087 }
00088 
00089 static cn_cbor_type mt_trans[] = {
00090   CN_CBOR_UINT,    CN_CBOR_INT,
00091   CN_CBOR_BYTES,   CN_CBOR_TEXT,
00092   CN_CBOR_ARRAY,   CN_CBOR_MAP,
00093   CN_CBOR_TAG,     CN_CBOR_SIMPLE,
00094 };
00095 
00096 struct parse_buf {
00097   unsigned char *buf;
00098   unsigned char *ebuf;
00099   cn_cbor_error err;
00100 };
00101 
00102 #define TAKE(pos, ebuf, n, stmt)                \
00103   if (n > (size_t)(ebuf - pos))                 \
00104     CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA);      \
00105   stmt;                                         \
00106   pos += n;
00107 
00108 static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) {
00109   unsigned char *pos = pb->buf;
00110   unsigned char *ebuf = pb->ebuf;
00111   cn_cbor* parent = top_parent;
00112   int ib;
00113   unsigned int mt;
00114   int ai;
00115   uint64_t val;
00116   cn_cbor* cb = NULL;
00117 #ifndef CBOR_NO_FLOAT
00118   union {
00119     float f;
00120     uint32_t u;
00121   } u32;
00122   union {
00123     double d;
00124     uint64_t u;
00125   } u64;
00126 #endif /* CBOR_NO_FLOAT */
00127 
00128 again:
00129   TAKE(pos, ebuf, 1, ib = ntoh8p(pos) );
00130   if (ib == IB_BREAK) {
00131     if (!(parent->flags & CN_CBOR_FL_INDEF))
00132       CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF);
00133     switch (parent->type) {
00134     case CN_CBOR_BYTES: case CN_CBOR_TEXT:
00135       parent->type += 2;            /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */
00136       break;
00137     case CN_CBOR_MAP:
00138       if (parent->length & 1)
00139         CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP);
00140     default:;
00141     }
00142     goto complete;
00143   }
00144   mt = ib >> 5;
00145   ai = ib & 0x1f;
00146   val = ai;
00147 
00148   cb = CN_CALLOC_CONTEXT();
00149   if (!cb)
00150     CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY);
00151 
00152   cb->type = mt_trans[mt];
00153 
00154   cb->parent = parent;
00155   if (parent->last_child) {
00156     parent->last_child->next = cb;
00157   } else {
00158     parent->first_child = cb;
00159   }
00160   parent->last_child = cb;
00161   parent->length++;
00162 
00163   switch (ai) {
00164   case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos))  ; cb->length = 1; break;
00165   case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; cb->length = 2; break;
00166   case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; cb->length = 4; break;
00167   case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; cb->length = 8; break;
00168 
00169   case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI);
00170   case AI_INDEF:
00171     if ((mt - MT_BYTES) <= MT_MAP) {
00172       cb->flags |= CN_CBOR_FL_INDEF;
00173       goto push;
00174     } else {
00175       CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF);
00176     }
00177   }
00178   // process content
00179   switch (mt) {
00180   case MT_UNSIGNED:
00181     cb->v.uint = val;           /* to do: Overflow check */
00182     /*
00183        if the integer itsef is used (ai=0..23), set the length to 4, for compliance   
00184        with the definition that integer values of mbed.UseBootstrap and mbed.MemoryTotalKB 
00185        is 4 bytes. 
00186        This implementation might cause bugs with other parameters that has no such requirement, 
00187        so the code should be changed in the future
00188     */
00189     if (cb->length == 0) {
00190         cb->length = 4;     
00191     }
00192     break;
00193   case MT_NEGATIVE:
00194     cb->v.sint = ~val;          /* to do: Overflow check */
00195     /*
00196        if the integer itsef is used (ai=0..23), set the length to 4, for compliance   
00197        with the definition that integer values of mbed.UseBootstrap and mbed.MemoryTotalKB 
00198        is 4 bytes. 
00199        This implementation might cause bugs with other parameters that has no such requirement, 
00200        so the code should be changed in the future
00201     */
00202     if (cb->length == 0) {
00203         cb->length = 4;     
00204     }
00205     break;
00206   case MT_BYTES: case MT_TEXT:
00207     cb->v.str = (char *) pos;
00208     cb->length = val;
00209     TAKE(pos, ebuf, val, ;);
00210     break;
00211   case MT_MAP:
00212     val <<= 1;
00213     /* fall through */
00214   case MT_ARRAY:
00215     if ((cb->v.count = val)) {
00216       cb->flags |= CN_CBOR_FL_COUNT;
00217       goto push;
00218     }
00219     break;
00220   case MT_TAG:
00221     cb->v.uint = val;
00222     goto push;
00223   case MT_PRIM:
00224     switch (ai) {
00225     case VAL_FALSE: cb->type = CN_CBOR_FALSE; break;
00226     case VAL_TRUE:  cb->type = CN_CBOR_TRUE;  break;
00227     case VAL_NIL:   cb->type = CN_CBOR_NULL;  break;
00228     case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break;
00229     case AI_2:
00230 #ifndef CBOR_NO_FLOAT
00231       cb->type = CN_CBOR_DOUBLE;
00232       cb->v.dbl = decode_half(val);
00233 #else /*  CBOR_NO_FLOAT */
00234       CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
00235 #endif /*  CBOR_NO_FLOAT */
00236       break;
00237     case AI_4:
00238 #ifndef CBOR_NO_FLOAT
00239       cb->type = CN_CBOR_DOUBLE;
00240       u32.u = val;
00241       cb->v.dbl = u32.f;
00242 #else /*  CBOR_NO_FLOAT */
00243       CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
00244 #endif /*  CBOR_NO_FLOAT */
00245       break;
00246     case AI_8:
00247 #ifndef CBOR_NO_FLOAT
00248       cb->type = CN_CBOR_DOUBLE;
00249       u64.u = val;
00250       cb->v.dbl = u64.d;
00251 #else /*  CBOR_NO_FLOAT */
00252       CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
00253 #endif /*  CBOR_NO_FLOAT */
00254       break;
00255     default: cb->v.uint = val;
00256     }
00257   }
00258 fill:                           /* emulate loops */
00259   if (parent->flags & CN_CBOR_FL_INDEF) {
00260     if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT)
00261       if (cb->type != parent->type)
00262           CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING);
00263     goto again;
00264   }
00265   if (parent->flags & CN_CBOR_FL_COUNT) {
00266     if (--parent->v.count)
00267       goto again;
00268   }
00269   /* so we are done filling parent. */
00270 complete:                       /* emulate return from call */
00271   if (parent == top_parent) {
00272     if (pos != ebuf)            /* XXX do this outside */
00273       CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED);
00274     pb->buf = pos;
00275     return cb;
00276   }
00277   cb = parent;
00278   parent = parent->parent;
00279   goto fill;
00280 push:                           /* emulate recursive call */
00281   parent = cb;
00282   goto again;
00283 fail:
00284   pb->buf = pos;
00285   return 0;
00286 }
00287 
00288 cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) {
00289   cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
00290   struct parse_buf pb;
00291   cn_cbor* ret;
00292 
00293   pb.buf  = (unsigned char *)buf;
00294   pb.ebuf = (unsigned char *)buf+len;
00295   pb.err  = CN_CBOR_NO_ERROR;
00296   ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher);
00297   if (ret != NULL) {
00298     /* mark as top node */
00299     ret->parent = NULL;
00300   } else {
00301 
00302     if (catcher.first_child) {
00303       catcher.first_child->parent = 0;
00304       cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM);
00305     }
00306 //fail:
00307     if (errp) {
00308       errp->err = pb.err;
00309       errp->pos = pb.buf - (unsigned char *)buf;
00310     }
00311     return NULL;
00312   }
00313   return ret;
00314 }
00315 
00316 
00317 #ifdef  __cplusplus
00318 }
00319 #endif
00320 
00321 #endif  /* CN_CBOR_C */