tinycbor: Import v0.2.1
[platform/upstream/iotivity.git] / extlibs / tinycbor / tinycbor / src / cborencoder.c
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 Intel Corporation
4 **
5 ** Permission is hereby granted, free of charge, to any person obtaining a copy
6 ** of this software and associated documentation files (the "Software"), to deal
7 ** in the Software without restriction, including without limitation the rights
8 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 ** copies of the Software, and to permit persons to whom the Software is
10 ** furnished to do so, subject to the following conditions:
11 **
12 ** The above copyright notice and this permission notice shall be included in
13 ** all copies or substantial portions of the Software.
14 **
15 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 ** THE SOFTWARE.
22 **
23 ****************************************************************************/
24
25 #define _BSD_SOURCE 1
26 #include "cbor.h"
27 #include "cborconstants_p.h"
28 #include "compilersupport_p.h"
29
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "assert_p.h"       /* Always include last */
35
36 void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags)
37 {
38     encoder->ptr = buffer;
39     encoder->end = buffer + size;
40     encoder->added = 0;
41     encoder->flags = flags;
42 }
43
44 static inline void put16(void *where, uint16_t v)
45 {
46     v = cbor_htons(v);
47     memcpy(where, &v, sizeof(v));
48 }
49
50 // Note: Since this is currently only used in situations where OOM is the only
51 // valid error, we KNOW this to be true.  Thus, this function now returns just 'true',
52 // but if in the future, any function starts returning a non-OOM error, this will need
53 // to be changed to the test.  At the moment, this is done to prevent more branches
54 // being created in the tinycbor output
55 static inline bool isOomError(CborError err)
56 {
57     (void) err;
58     return true;
59 }
60
61 static inline void put32(void *where, uint32_t v)
62 {
63     v = cbor_htonl(v);
64     memcpy(where, &v, sizeof(v));
65 }
66
67 static inline void put64(void *where, uint64_t v)
68 {
69     v = cbor_htonll(v);
70     memcpy(where, &v, sizeof(v));
71 }
72
73 static inline bool would_overflow(CborEncoder *encoder, size_t len)
74 {
75     ptrdiff_t remaining = (ptrdiff_t)encoder->end;
76     remaining -= remaining ? (ptrdiff_t)encoder->ptr : encoder->bytes_needed;
77     remaining -= (ptrdiff_t)len;
78     return unlikely(remaining < 0);
79 }
80
81 static inline void advance_ptr(CborEncoder *encoder, size_t n)
82 {
83     if (encoder->end)
84         encoder->ptr += n;
85     else
86         encoder->bytes_needed += n;
87 }
88
89 static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len)
90 {
91     if (would_overflow(encoder, len)) {
92         if (encoder->end != NULL) {
93             len -= encoder->end - encoder->ptr;
94             encoder->end = NULL;
95             encoder->bytes_needed = 0;
96         }
97
98         advance_ptr(encoder, len);
99         return CborErrorOutOfMemory;
100     }
101
102     memcpy(encoder->ptr, data, len);
103     encoder->ptr += len;
104     return CborNoError;
105 }
106
107 static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte)
108 {
109     return append_to_buffer(encoder, &byte, 1);
110 }
111
112 static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
113 {
114     /* Little-endian would have been so much more convenient here:
115      * We could just write at the beginning of buf but append_to_buffer
116      * only the necessary bytes.
117      * Since it has to be big endian, do it the other way around:
118      * write from the end. */
119     uint64_t buf[2];
120     uint8_t *const bufend = (uint8_t *)buf + sizeof(buf);
121     uint8_t *bufstart = bufend - 1;
122     put64(buf + 1, ui);     // we probably have a bunch of zeros in the beginning
123
124     if (ui < Value8Bit) {
125         *bufstart += shiftedMajorType;
126     } else {
127         unsigned more = 0;
128         if (ui > 0xffU)
129             ++more;
130         if (ui > 0xffffU)
131             ++more;
132         if (ui > 0xffffffffU)
133             ++more;
134         bufstart -= (size_t)1 << more;
135         *bufstart = shiftedMajorType + Value8Bit + more;
136     }
137
138     return append_to_buffer(encoder, bufstart, bufend - bufstart);
139 }
140
141 static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
142 {
143     ++encoder->added;
144     return encode_number_no_update(encoder, ui, shiftedMajorType);
145 }
146
147
148 CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
149 {
150     return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
151 }
152
153 CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
154 {
155     return encode_number(encoder, absolute_value, NegativeIntegerType << MajorTypeShift);
156 }
157
158 CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
159 {
160     // adapted from code in RFC 7049 appendix C (pseudocode)
161     uint64_t ui = value >> 63;              // extend sign to whole length
162     uint8_t majorType = ui & 0x20;          // extract major type
163     ui ^= value;                            // complement negatives
164     return encode_number(encoder, ui, majorType);
165 }
166
167 CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
168 {
169 #ifndef CBOR_ENCODER_NO_CHECK_USER
170     // check if this is a valid simple type
171     if (value >= HalfPrecisionFloat && value <= Break)
172         return CborErrorIllegalSimpleType;
173 #endif
174     return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
175 }
176
177 CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
178 {
179     uint8_t buf[1 + sizeof(uint64_t)];
180     assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
181     buf[0] = fpType;
182
183     unsigned size = 2U << (fpType - CborHalfFloatType);
184     if (size == 8)
185         put64(buf + 1, *(const uint64_t*)value);
186     else if (size == 4)
187         put32(buf + 1, *(const uint32_t*)value);
188     else
189         put16(buf + 1, *(const uint16_t*)value);
190     ++encoder->added;
191     return append_to_buffer(encoder, buf, size + 1);
192 }
193
194 CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
195 {
196     // tags don't count towards the number of elements in an array or map
197     return encode_number_no_update(encoder, tag, TagType << MajorTypeShift);
198 }
199
200 static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
201 {
202     CborError err = encode_number(encoder, length, shiftedMajorType);
203     if (err && !isOomError(err))
204         return err;
205     return append_to_buffer(encoder, string, length);
206 }
207
208 CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
209 {
210     return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
211 }
212
213 CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
214 {
215     return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
216 }
217
218 #ifdef __GNUC__
219 __attribute__((noinline))
220 #endif
221 static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
222 {
223     CborError err;
224     container->ptr = encoder->ptr;
225     container->end = encoder->end;
226     ++encoder->added;
227     container->added = 0;
228
229     cbor_static_assert(((MapType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == CborIteratorFlag_ContainerIsMap);
230     cbor_static_assert(((ArrayType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == 0);
231     container->flags = shiftedMajorType & CborIteratorFlag_ContainerIsMap;
232
233     if (length == CborIndefiniteLength) {
234         container->flags |= CborIteratorFlag_UnknownLength;
235         err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength);
236     } else {
237         err = encode_number_no_update(container, length, shiftedMajorType);
238     }
239     if (err && !isOomError(err))
240         return err;
241
242     return CborNoError;
243 }
244
245 CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
246 {
247     return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift);
248 }
249
250 CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
251 {
252     if (length != CborIndefiniteLength && length > SIZE_MAX / 2)
253         return CborErrorDataTooLarge;
254     return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift);
255 }
256
257 CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
258 {
259     if (encoder->end)
260         encoder->ptr = containerEncoder->ptr;
261     else
262         encoder->bytes_needed = containerEncoder->bytes_needed;
263     encoder->end = containerEncoder->end;
264     if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
265         return append_byte_to_buffer(encoder, BreakByte);
266     return CborNoError;
267 }