blob: 8ef4a43659c232f51b57368fa65709c68ecb760d [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
Thiago Macieiradbc01292016-06-06 17:02:25 -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 */
Erich Keane47a78562015-08-10 15:19:50 -070055static 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 Macieira618d2f12015-09-22 09:14:29 -0700109 return append_to_buffer(encoder, &byte, 1);
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900110}
111
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700112static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700113{
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700114 /* 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. */
Thiago Macieira510e5b82015-09-21 16:05:02 -0700119 uint64_t buf[2];
120 uint8_t *const bufend = (uint8_t *)buf + sizeof(buf);
Thiago Macieira5752ce52015-06-16 12:10:03 -0700121 uint8_t *bufstart = bufend - 1;
Thiago Macieiradbc01292016-06-06 17:02:25 -0700122 put64(buf + 1, ui); /* we probably have a bunch of zeros in the beginning */
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700123
124 if (ui < Value8Bit) {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700125 *bufstart += shiftedMajorType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700126 } else {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700127 unsigned more = 0;
Thiago Macieira13c85792015-07-07 16:15:41 -0700128 if (ui > 0xffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700129 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700130 if (ui > 0xffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700131 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700132 if (ui > 0xffffffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700133 ++more;
Thiago Macieirae12dfd02016-06-07 16:29:25 -0700134 bufstart -= (size_t)1 << more;
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700135 *bufstart = shiftedMajorType + Value8Bit + more;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700136 }
137
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700138 return append_to_buffer(encoder, bufstart, bufend - bufstart);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700139}
140
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700141static 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
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700148CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
149{
150 return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
151}
152
Thiago Macieira78632b32015-09-26 14:18:48 -0700153CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
154{
155 return encode_number(encoder, absolute_value, NegativeIntegerType << MajorTypeShift);
156}
157
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700158CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
159{
Thiago Macieiradbc01292016-06-06 17:02:25 -0700160 /* 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 */
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700164 return encode_number(encoder, ui, majorType);
165}
166
167CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
168{
169#ifndef CBOR_ENCODER_NO_CHECK_USER
Thiago Macieiradbc01292016-06-06 17:02:25 -0700170 /* check if this is a valid simple type */
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700171 if (value >= HalfPrecisionFloat && value <= Break)
172 return CborErrorIllegalSimpleType;
173#endif
174 return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
175}
176
Thiago Macieira81f33432015-07-02 16:16:28 -0700177CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700178{
Thiago Macieira81f33432015-07-02 16:16:28 -0700179 uint8_t buf[1 + sizeof(uint64_t)];
180 assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
181 buf[0] = fpType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700182
Thiago Macieira81f33432015-07-02 16:16:28 -0700183 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);
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700190 ++encoder->added;
Thiago Macieira81f33432015-07-02 16:16:28 -0700191 return append_to_buffer(encoder, buf, size + 1);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700192}
Thiago Macieira8f98a112015-05-08 17:25:29 -0700193
194CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
195{
Thiago Macieiradbc01292016-06-06 17:02:25 -0700196 /* tags don't count towards the number of elements in an array or map */
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700197 return encode_number_no_update(encoder, tag, TagType << MajorTypeShift);
Thiago Macieira8f98a112015-05-08 17:25:29 -0700198}
Thiago Macieirab54debe2015-05-08 17:42:39 -0700199
Thiago Macieira5752ce52015-06-16 12:10:03 -0700200static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700201{
202 CborError err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700203 if (err && !isOomError(err))
Thiago Macieirab54debe2015-05-08 17:42:39 -0700204 return err;
205 return append_to_buffer(encoder, string, length);
206}
207
Thiago Macieira5752ce52015-06-16 12:10:03 -0700208CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700209{
210 return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
211}
212
213CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
214{
215 return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
216}
Thiago Macieira355817e2015-05-08 18:38:18 -0700217
Thiago Macieira56635242015-09-20 17:44:51 -0700218#ifdef __GNUC__
219__attribute__((noinline))
220#endif
221static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
Thiago Macieira355817e2015-05-08 18:38:18 -0700222{
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900223 CborError err;
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700224 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 }
Erich Keane47a78562015-08-10 15:19:50 -0700239 if (err && !isOomError(err))
Thiago Macieira355817e2015-05-08 18:38:18 -0700240 return err;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900241
Thiago Macieira355817e2015-05-08 18:38:18 -0700242 return CborNoError;
243}
244
245CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
246{
Thiago Macieira56635242015-09-20 17:44:51 -0700247 return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700248}
249
250CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
251{
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700252 if (length != CborIndefiniteLength && length > SIZE_MAX / 2)
253 return CborErrorDataTooLarge;
Thiago Macieira56635242015-09-20 17:44:51 -0700254 return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700255}
256
257CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
258{
Thiago Macieira397f9792015-09-18 19:36:26 -0700259 if (encoder->end)
260 encoder->ptr = containerEncoder->ptr;
261 else
262 encoder->bytes_needed = containerEncoder->bytes_needed;
Erich Keane47a78562015-08-10 15:19:50 -0700263 encoder->end = containerEncoder->end;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900264 if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
265 return append_byte_to_buffer(encoder, BreakByte);
Thiago Macieira355817e2015-05-08 18:38:18 -0700266 return CborNoError;
267}