• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef CN_ENCODER_C
2 #define CN_ENCODER_C
3 
4 #ifdef  __cplusplus
5 extern "C" {
6 #endif
7 #ifdef EMACS_INDENTATION_HELPER
8 } /* Duh. */
9 #endif
10 
11 #include <arpa/inet.h>
12 #include <string.h>
13 #include <strings.h>
14 #include <stdbool.h>
15 #include <assert.h>
16 
17 #include "cn-cbor/cn-cbor.h"
18 #include "cbor.h"
19 
20 #define hton8p(p) (*(uint8_t*)(p))
21 #define hton16p(p) (htons(*(uint16_t*)(p)))
22 #define hton32p(p) (htonl(*(uint32_t*)(p)))
hton64p(const uint8_t * p)23 static uint64_t hton64p(const uint8_t *p) {
24   /* TODO: does this work on both BE and LE systems? */
25   uint64_t ret = hton32p(p);
26   ret <<= 32;
27   ret |= hton32p(p+4);
28   return ret;
29 }
30 
31 typedef struct _write_state
32 {
33   uint8_t *buf;
34   ssize_t offset;
35   ssize_t size;
36 } cn_write_state;
37 
38 #define ensure_writable(sz) if ((ws->offset<0) || (ws->offset + (sz) >= ws->size)) { \
39   ws->offset = -1; \
40   return; \
41 }
42 
43 #define write_byte_and_data(b, data, sz) \
44 ws->buf[ws->offset++] = (b); \
45 memcpy(ws->buf+ws->offset, (data), (sz)); \
46 ws->offset += sz;
47 
48 #define write_byte(b) \
49 ws->buf[ws->offset++] = (b); \
50 
51 #define write_byte_ensured(b) \
52 ensure_writable(1); \
53 write_byte(b); \
54 
55 static uint8_t _xlate[] = {
56   IB_FALSE,    /* CN_CBOR_FALSE */
57   IB_TRUE,     /* CN_CBOR_TRUE */
58   IB_NIL,      /* CN_CBOR_NULL */
59   IB_UNDEF,    /* CN_CBOR_UNDEF */
60   IB_UNSIGNED, /* CN_CBOR_UINT */
61   IB_NEGATIVE, /* CN_CBOR_INT */
62   IB_BYTES,    /* CN_CBOR_BYTES */
63   IB_TEXT,     /* CN_CBOR_TEXT */
64   IB_BYTES,    /* CN_CBOR_BYTES_CHUNKED */
65   IB_TEXT,     /* CN_CBOR_TEXT_CHUNKED */
66   IB_ARRAY,    /* CN_CBOR_ARRAY */
67   IB_MAP,      /* CN_CBOR_MAP */
68   IB_TAG,      /* CN_CBOR_TAG */
69   IB_PRIM,     /* CN_CBOR_SIMPLE */
70   0xFF,        /* CN_CBOR_DOUBLE */
71   0xFF         /* CN_CBOR_INVALID */
72 };
73 
is_indefinite(const cn_cbor * cb)74 static inline bool is_indefinite(const cn_cbor *cb)
75 {
76   return (cb->flags & CN_CBOR_FL_INDEF) != 0;
77 }
78 
_write_positive(cn_write_state * ws,cn_cbor_type typ,uint64_t val)79 static void _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val) {
80   uint8_t ib;
81 
82   assert((size_t)typ < sizeof(_xlate));
83 
84   ib = _xlate[typ];
85   if (ib == 0xFF) {
86     ws->offset = -1;
87     return;
88   }
89 
90   if (val < 24) {
91     ensure_writable(1);
92     write_byte(ib | val);
93   } else if (val < 256) {
94     ensure_writable(2);
95     write_byte(ib | 24);
96     write_byte((uint8_t)val);
97   } else if (val < 65536) {
98     uint16_t be16 = (uint16_t)val;
99     ensure_writable(3);
100     be16 = hton16p(&be16);
101     write_byte_and_data(ib | 25, (const void*)&be16, 2);
102   } else if (val < 0x100000000L) {
103     uint32_t be32 = (uint32_t)val;
104     ensure_writable(5);
105     be32 = hton32p(&be32);
106     write_byte_and_data(ib | 26, (const void*)&be32, 4);
107   } else {
108     uint64_t be64;
109     ensure_writable(9);
110     be64 = hton64p((const uint8_t*)&val);
111     write_byte_and_data(ib | 27, (const void*)&be64, 8);
112   }
113 }
114 
115 #ifndef CBOR_NO_FLOAT
_write_double(cn_write_state * ws,double val)116 static void _write_double(cn_write_state *ws, double val)
117 {
118   float float_val = val;
119   if (float_val == val) {                /* 32 bits is enough and we aren't NaN */
120     uint32_t be32;
121     uint16_t be16, u16;
122     union {
123       float f;
124       uint32_t u;
125     } u32;
126     u32.f = float_val;
127     if ((u32.u & 0x1FFF) == 0) { /* worth trying half */
128       int s16 = (u32.u >> 16) & 0x8000;
129       int exp = (u32.u >> 23) & 0xff;
130       int mant = u32.u & 0x7fffff;
131       if (exp == 0 && mant == 0)
132         ;              /* 0.0, -0.0 */
133       else if (exp >= 113 && exp <= 142) /* normalized */
134         s16 += ((exp - 112) << 10) + (mant >> 13);
135       else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */
136         if (mant & ((1 << (126 - exp)) - 1))
137           goto float32;         /* loss of precision */
138         s16 += ((mant + 0x800000) >> (126 - exp));
139       } else if (exp == 255 && mant == 0) { /* Inf */
140         s16 += 0x7c00;
141       } else
142         goto float32;           /* loss of range */
143 
144       ensure_writable(3);
145       u16 = s16;
146       be16 = hton16p((const uint8_t*)&u16);
147 
148       write_byte_and_data(IB_PRIM | 25, (const void*)&be16, 2);
149       return;
150     }
151   float32:
152     ensure_writable(5);
153     be32 = hton32p((const uint8_t*)&u32.u);
154 
155     write_byte_and_data(IB_PRIM | 26, (const void*)&be32, 4);
156 
157   } else if (val != val) {      /* NaN -- we always write a half NaN*/
158     ensure_writable(3);
159     write_byte_and_data(IB_PRIM | 25, (const void*)"\x7e\x00", 2);
160   } else {
161     uint64_t be64;
162     /* Copy the same problematic implementation from the decoder. */
163     union {
164       double d;
165       uint64_t u;
166     } u64;
167 
168     u64.d = val;
169 
170     ensure_writable(9);
171     be64 = hton64p((const uint8_t*)&u64.u);
172 
173     write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8);
174 
175   }
176 }
177 #endif /* CBOR_NO_FLOAT */
178 
179 // TODO: make public?
180 typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
_visit(const cn_cbor * cb,cn_visit_func visitor,cn_visit_func breaker,void * context)181 static void _visit(const cn_cbor *cb,
182                    cn_visit_func visitor,
183                    cn_visit_func breaker,
184                    void *context)
185 {
186     const cn_cbor *p = cb;
187     int depth = 0;
188     while (p)
189     {
190 visit:
191       visitor(p, depth, context);
192       if (p->first_child) {
193         p = p->first_child;
194         depth++;
195       } else{
196         // Empty indefinite
197         if (is_indefinite(p)) {
198           breaker(p->parent, depth, context);
199         }
200         if (p->next) {
201           p = p->next;
202         } else {
203           while (p->parent) {
204             depth--;
205             if (is_indefinite(p->parent)) {
206               breaker(p->parent, depth, context);
207             }
208             if (p->parent->next) {
209               p = p->parent->next;
210               goto visit;
211             }
212             p = p->parent;
213           }
214           return;
215         }
216       }
217     }
218 }
219 
220 #define CHECK(st) (st); \
221 if (ws->offset < 0) { return; }
222 
_encoder_visitor(const cn_cbor * cb,int depth,void * context)223 void _encoder_visitor(const cn_cbor *cb, int depth, void *context)
224 {
225   cn_write_state *ws = context;
226   UNUSED_PARAM(depth);
227 
228   switch (cb->type) {
229   case CN_CBOR_ARRAY:
230     if (is_indefinite(cb)) {
231       write_byte_ensured(IB_ARRAY | AI_INDEF);
232     } else {
233       CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length));
234     }
235     break;
236   case CN_CBOR_MAP:
237     if (is_indefinite(cb)) {
238       write_byte_ensured(IB_MAP | AI_INDEF);
239     } else {
240       CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length/2));
241     }
242     break;
243   case CN_CBOR_BYTES_CHUNKED:
244   case CN_CBOR_TEXT_CHUNKED:
245     write_byte_ensured(_xlate[cb->type] | AI_INDEF);
246     break;
247 
248   case CN_CBOR_TEXT:
249   case CN_CBOR_BYTES:
250     CHECK(_write_positive(ws, cb->type, cb->length));
251     ensure_writable(cb->length);
252     memcpy(ws->buf+ws->offset, cb->v.str, cb->length);
253     ws->offset += cb->length;
254     break;
255 
256   case CN_CBOR_FALSE:
257   case CN_CBOR_TRUE:
258   case CN_CBOR_NULL:
259   case CN_CBOR_UNDEF:
260     write_byte_ensured(_xlate[cb->type]);
261     break;
262 
263   case CN_CBOR_TAG:
264   case CN_CBOR_UINT:
265   case CN_CBOR_SIMPLE:
266     CHECK(_write_positive(ws, cb->type, cb->v.uint));
267     break;
268 
269   case CN_CBOR_INT:
270     assert(cb->v.sint < 0);
271     CHECK(_write_positive(ws, CN_CBOR_INT, ~(cb->v.sint)));
272     break;
273 
274   case CN_CBOR_DOUBLE:
275 #ifndef CBOR_NO_FLOAT
276     CHECK(_write_double(ws, cb->v.dbl));
277 #endif /* CBOR_NO_FLOAT */
278     break;
279   case CN_CBOR_FLOAT:
280 #ifndef CBOR_NO_FLOAT
281     CHECK(_write_double(ws, cb->v.f));
282 #endif /* CBOR_NO_FLOAT */
283     break;
284 
285   case CN_CBOR_INVALID:
286     ws->offset = -1;
287     break;
288   }
289 }
290 
_encoder_breaker(const cn_cbor * cb,int depth,void * context)291 void _encoder_breaker(const cn_cbor *cb, int depth, void *context)
292 {
293   cn_write_state *ws = context;
294   UNUSED_PARAM(cb);
295   UNUSED_PARAM(depth);
296   write_byte_ensured(IB_BREAK);
297 }
298 
cn_cbor_encoder_write(uint8_t * buf,size_t buf_offset,size_t buf_size,const cn_cbor * cb)299 ssize_t cn_cbor_encoder_write(uint8_t *buf,
300 			      size_t buf_offset,
301 			      size_t buf_size,
302 			      const cn_cbor *cb)
303 {
304   cn_write_state ws = { buf, buf_offset, buf_size };
305   _visit(cb, _encoder_visitor, _encoder_breaker, &ws);
306   if (ws.offset < 0) { return -1; }
307   return ws.offset - buf_offset;
308 }
309 
310 #ifdef  __cplusplus
311 }
312 #endif
313 
314 #endif  /* CN_CBOR_C */
315