blob: 7bed5835dc0a4aab0000651555ce821a6421c084 [file] [log] [blame]
Thiago Macieiraf1cadf02015-05-08 17:19:17 -07001/****************************************************************************
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
Thiago Macieiraed5b57c2015-07-07 16:38:27 -070025#define _BSD_SOURCE 1
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070026#include "cbor.h"
27#include "cborconstants_p.h"
28#include "compilersupport_p.h"
29
30#include <assert.h>
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070031#include <stdlib.h>
32#include <string.h>
33
Thiago Macieira8f3fb782015-06-16 16:27:01 -070034#include "assert_p.h" /* Always include last */
35
Thiago Macieira5752ce52015-06-16 12:10:03 -070036void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070037{
38 encoder->ptr = buffer;
39 encoder->end = buffer + size;
Thiago Macieira4e9626c2015-09-21 14:57:17 -070040 encoder->added = 0;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070041 encoder->flags = flags;
42}
43
Thiago Macieira5752ce52015-06-16 12:10:03 -070044static inline void put16(void *where, uint16_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070045{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070046 v = cbor_htons(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070047 memcpy(where, &v, sizeof(v));
48}
49
Erich Keane47a78562015-08-10 15:19:50 -070050// 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
55static inline bool isOomError(CborError err)
56{
57 (void) err;
58 return true;
59}
60
Thiago Macieira5752ce52015-06-16 12:10:03 -070061static inline void put32(void *where, uint32_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070062{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070063 v = cbor_htonl(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070064 memcpy(where, &v, sizeof(v));
65}
66
Thiago Macieira5752ce52015-06-16 12:10:03 -070067static inline void put64(void *where, uint64_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070068{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070069 v = cbor_htonll(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070070 memcpy(where, &v, sizeof(v));
71}
72
Thiago Macieira397f9792015-09-18 19:36:26 -070073static 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
81static 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
Thiago Macieira5752ce52015-06-16 12:10:03 -070089static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070090{
Thiago Macieira397f9792015-09-18 19:36:26 -070091 if (would_overflow(encoder, len)) {
Erich Keane47a78562015-08-10 15:19:50 -070092 if (encoder->end != NULL) {
93 len -= encoder->end - encoder->ptr;
Thiago Macieira397f9792015-09-18 19:36:26 -070094 encoder->end = NULL;
95 encoder->bytes_needed = 0;
Erich Keane47a78562015-08-10 15:19:50 -070096 }
97
Thiago Macieira397f9792015-09-18 19:36:26 -070098 advance_ptr(encoder, len);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070099 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -0700100 }
101
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700102 memcpy(encoder->ptr, data, len);
103 encoder->ptr += len;
104 return CborNoError;
105}
106
Thiago Macieira5752ce52015-06-16 12:10:03 -0700107static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte)
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900108{
Thiago Macieira397f9792015-09-18 19:36:26 -0700109 if (would_overflow(encoder, 1)) {
Erich Keane47a78562015-08-10 15:19:50 -0700110 if (encoder->end != NULL) {
Thiago Macieira397f9792015-09-18 19:36:26 -0700111 encoder->end = NULL;
112 encoder->bytes_needed = 0;
Erich Keane47a78562015-08-10 15:19:50 -0700113 }
114
Thiago Macieira397f9792015-09-18 19:36:26 -0700115 advance_ptr(encoder, 1);
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900116 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -0700117 }
118
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900119 *encoder->ptr++ = byte;
120 return CborNoError;
121}
122
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700123static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700124{
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700125 /* Little-endian would have been so much more convenient here:
126 * We could just write at the beginning of buf but append_to_buffer
127 * only the necessary bytes.
128 * Since it has to be big endian, do it the other way around:
129 * write from the end. */
Thiago Macieira510e5b82015-09-21 16:05:02 -0700130 uint64_t buf[2];
131 uint8_t *const bufend = (uint8_t *)buf + sizeof(buf);
Thiago Macieira5752ce52015-06-16 12:10:03 -0700132 uint8_t *bufstart = bufend - 1;
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700133 put64(buf + 1, ui); // we probably have a bunch of zeros in the beginning
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700134
135 if (ui < Value8Bit) {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700136 *bufstart += shiftedMajorType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700137 } else {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700138 unsigned more = 0;
Thiago Macieira13c85792015-07-07 16:15:41 -0700139 if (ui > 0xffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700140 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700141 if (ui > 0xffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700142 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700143 if (ui > 0xffffffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700144 ++more;
145 bufstart -= 1 << more;
146 *bufstart = shiftedMajorType + Value8Bit + more;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700147 }
148
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700149 return append_to_buffer(encoder, bufstart, bufend - bufstart);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700150}
151
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700152static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
153{
154 ++encoder->added;
155 return encode_number_no_update(encoder, ui, shiftedMajorType);
156}
157
158
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700159CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
160{
161 return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
162}
163
Thiago Macieira78632b32015-09-26 14:18:48 -0700164CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
165{
166 return encode_number(encoder, absolute_value, NegativeIntegerType << MajorTypeShift);
167}
168
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700169CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
170{
171 // adapted from code in RFC 7049 appendix C (pseudocode)
172 uint64_t ui = value >> 63; // extend sign to whole length
173 uint8_t majorType = ui & 0x20; // extract major type
174 ui ^= value; // complement negatives
175 return encode_number(encoder, ui, majorType);
176}
177
178CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
179{
180#ifndef CBOR_ENCODER_NO_CHECK_USER
181 // check if this is a valid simple type
182 if (value >= HalfPrecisionFloat && value <= Break)
183 return CborErrorIllegalSimpleType;
184#endif
185 return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
186}
187
Thiago Macieira81f33432015-07-02 16:16:28 -0700188CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700189{
Thiago Macieira81f33432015-07-02 16:16:28 -0700190 uint8_t buf[1 + sizeof(uint64_t)];
191 assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
192 buf[0] = fpType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700193
Thiago Macieira81f33432015-07-02 16:16:28 -0700194 unsigned size = 2U << (fpType - CborHalfFloatType);
195 if (size == 8)
196 put64(buf + 1, *(const uint64_t*)value);
197 else if (size == 4)
198 put32(buf + 1, *(const uint32_t*)value);
199 else
200 put16(buf + 1, *(const uint16_t*)value);
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700201 ++encoder->added;
Thiago Macieira81f33432015-07-02 16:16:28 -0700202 return append_to_buffer(encoder, buf, size + 1);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700203}
Thiago Macieira8f98a112015-05-08 17:25:29 -0700204
205CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
206{
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700207 // tags don't count towards the number of elements in an array or map
208 return encode_number_no_update(encoder, tag, TagType << MajorTypeShift);
Thiago Macieira8f98a112015-05-08 17:25:29 -0700209}
Thiago Macieirab54debe2015-05-08 17:42:39 -0700210
Thiago Macieira5752ce52015-06-16 12:10:03 -0700211static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700212{
213 CborError err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700214 if (err && !isOomError(err))
Thiago Macieirab54debe2015-05-08 17:42:39 -0700215 return err;
216 return append_to_buffer(encoder, string, length);
217}
218
Thiago Macieira5752ce52015-06-16 12:10:03 -0700219CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700220{
221 return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
222}
223
224CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
225{
226 return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
227}
Thiago Macieira355817e2015-05-08 18:38:18 -0700228
Thiago Macieira56635242015-09-20 17:44:51 -0700229#ifdef __GNUC__
230__attribute__((noinline))
231#endif
232static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
Thiago Macieira355817e2015-05-08 18:38:18 -0700233{
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900234 CborError err;
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700235 container->ptr = encoder->ptr;
236 container->end = encoder->end;
237 ++encoder->added;
238 container->added = 0;
239
240 cbor_static_assert(((MapType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == CborIteratorFlag_ContainerIsMap);
241 cbor_static_assert(((ArrayType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == 0);
242 container->flags = shiftedMajorType & CborIteratorFlag_ContainerIsMap;
243
244 if (length == CborIndefiniteLength) {
245 container->flags |= CborIteratorFlag_UnknownLength;
246 err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength);
247 } else {
248 err = encode_number_no_update(container, length, shiftedMajorType);
249 }
Erich Keane47a78562015-08-10 15:19:50 -0700250 if (err && !isOomError(err))
Thiago Macieira355817e2015-05-08 18:38:18 -0700251 return err;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900252
Thiago Macieira355817e2015-05-08 18:38:18 -0700253 return CborNoError;
254}
255
256CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
257{
Thiago Macieira56635242015-09-20 17:44:51 -0700258 return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700259}
260
261CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
262{
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700263 if (length != CborIndefiniteLength && length > SIZE_MAX / 2)
264 return CborErrorDataTooLarge;
Thiago Macieira56635242015-09-20 17:44:51 -0700265 return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700266}
267
268CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
269{
Thiago Macieira397f9792015-09-18 19:36:26 -0700270 if (encoder->end)
271 encoder->ptr = containerEncoder->ptr;
272 else
273 encoder->bytes_needed = containerEncoder->bytes_needed;
Erich Keane47a78562015-08-10 15:19:50 -0700274 encoder->end = containerEncoder->end;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900275 if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
276 return append_byte_to_buffer(encoder, BreakByte);
Thiago Macieira355817e2015-05-08 18:38:18 -0700277 return CborNoError;
278}