blob: 8c7078e2da8942aa07860435d89a4a15c904a067 [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
Otavio Pontese2d5dd52016-07-08 09:49:38 -030026#define _DEFAULT_SOURCE 1
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070027#include "cbor.h"
28#include "cborconstants_p.h"
29#include "compilersupport_p.h"
30
31#include <assert.h>
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070032#include <stdlib.h>
33#include <string.h>
34
Thiago Macieira8f3fb782015-06-16 16:27:01 -070035#include "assert_p.h" /* Always include last */
36
Thiago Macieira5752ce52015-06-16 12:10:03 -070037void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070038{
39 encoder->ptr = buffer;
40 encoder->end = buffer + size;
Thiago Macieira4e9626c2015-09-21 14:57:17 -070041 encoder->added = 0;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070042 encoder->flags = flags;
43}
44
Thiago Macieira5752ce52015-06-16 12:10:03 -070045static inline void put16(void *where, uint16_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070046{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070047 v = cbor_htons(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070048 memcpy(where, &v, sizeof(v));
49}
50
Thiago Macieiradbc01292016-06-06 17:02:25 -070051/* Note: Since this is currently only used in situations where OOM is the only
52 * valid error, we KNOW this to be true. Thus, this function now returns just 'true',
53 * but if in the future, any function starts returning a non-OOM error, this will need
54 * to be changed to the test. At the moment, this is done to prevent more branches
55 * being created in the tinycbor output */
Erich Keane47a78562015-08-10 15:19:50 -070056static inline bool isOomError(CborError err)
57{
58 (void) err;
59 return true;
60}
61
Thiago Macieira5752ce52015-06-16 12:10:03 -070062static inline void put32(void *where, uint32_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070063{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070064 v = cbor_htonl(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070065 memcpy(where, &v, sizeof(v));
66}
67
Thiago Macieira5752ce52015-06-16 12:10:03 -070068static inline void put64(void *where, uint64_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070069{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070070 v = cbor_htonll(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070071 memcpy(where, &v, sizeof(v));
72}
73
Thiago Macieira397f9792015-09-18 19:36:26 -070074static inline bool would_overflow(CborEncoder *encoder, size_t len)
75{
76 ptrdiff_t remaining = (ptrdiff_t)encoder->end;
77 remaining -= remaining ? (ptrdiff_t)encoder->ptr : encoder->bytes_needed;
78 remaining -= (ptrdiff_t)len;
79 return unlikely(remaining < 0);
80}
81
82static inline void advance_ptr(CborEncoder *encoder, size_t n)
83{
84 if (encoder->end)
85 encoder->ptr += n;
86 else
87 encoder->bytes_needed += n;
88}
89
Thiago Macieira5752ce52015-06-16 12:10:03 -070090static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070091{
Thiago Macieira397f9792015-09-18 19:36:26 -070092 if (would_overflow(encoder, len)) {
Erich Keane47a78562015-08-10 15:19:50 -070093 if (encoder->end != NULL) {
94 len -= encoder->end - encoder->ptr;
Thiago Macieira397f9792015-09-18 19:36:26 -070095 encoder->end = NULL;
96 encoder->bytes_needed = 0;
Erich Keane47a78562015-08-10 15:19:50 -070097 }
98
Thiago Macieira397f9792015-09-18 19:36:26 -070099 advance_ptr(encoder, len);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700100 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -0700101 }
102
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700103 memcpy(encoder->ptr, data, len);
104 encoder->ptr += len;
105 return CborNoError;
106}
107
Thiago Macieira5752ce52015-06-16 12:10:03 -0700108static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte)
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900109{
Thiago Macieira618d2f12015-09-22 09:14:29 -0700110 return append_to_buffer(encoder, &byte, 1);
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900111}
112
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700113static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700114{
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700115 /* Little-endian would have been so much more convenient here:
116 * We could just write at the beginning of buf but append_to_buffer
117 * only the necessary bytes.
118 * Since it has to be big endian, do it the other way around:
119 * write from the end. */
Thiago Macieira510e5b82015-09-21 16:05:02 -0700120 uint64_t buf[2];
121 uint8_t *const bufend = (uint8_t *)buf + sizeof(buf);
Thiago Macieira5752ce52015-06-16 12:10:03 -0700122 uint8_t *bufstart = bufend - 1;
Thiago Macieiradbc01292016-06-06 17:02:25 -0700123 put64(buf + 1, ui); /* we probably have a bunch of zeros in the beginning */
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700124
125 if (ui < Value8Bit) {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700126 *bufstart += shiftedMajorType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700127 } else {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700128 unsigned more = 0;
Thiago Macieira13c85792015-07-07 16:15:41 -0700129 if (ui > 0xffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700130 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700131 if (ui > 0xffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700132 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700133 if (ui > 0xffffffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700134 ++more;
Thiago Macieirae12dfd02016-06-07 16:29:25 -0700135 bufstart -= (size_t)1 << more;
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700136 *bufstart = shiftedMajorType + Value8Bit + more;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700137 }
138
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700139 return append_to_buffer(encoder, bufstart, bufend - bufstart);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700140}
141
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700142static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
143{
144 ++encoder->added;
145 return encode_number_no_update(encoder, ui, shiftedMajorType);
146}
147
148
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700149CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
150{
151 return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
152}
153
Thiago Macieira78632b32015-09-26 14:18:48 -0700154CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
155{
156 return encode_number(encoder, absolute_value, NegativeIntegerType << MajorTypeShift);
157}
158
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700159CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
160{
Thiago Macieiradbc01292016-06-06 17:02:25 -0700161 /* adapted from code in RFC 7049 appendix C (pseudocode) */
162 uint64_t ui = value >> 63; /* extend sign to whole length */
163 uint8_t majorType = ui & 0x20; /* extract major type */
164 ui ^= value; /* complement negatives */
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700165 return encode_number(encoder, ui, majorType);
166}
167
168CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
169{
170#ifndef CBOR_ENCODER_NO_CHECK_USER
Thiago Macieiradbc01292016-06-06 17:02:25 -0700171 /* check if this is a valid simple type */
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700172 if (value >= HalfPrecisionFloat && value <= Break)
173 return CborErrorIllegalSimpleType;
174#endif
175 return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
176}
177
Thiago Macieira81f33432015-07-02 16:16:28 -0700178CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700179{
Thiago Macieira81f33432015-07-02 16:16:28 -0700180 uint8_t buf[1 + sizeof(uint64_t)];
181 assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
182 buf[0] = fpType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700183
Thiago Macieira81f33432015-07-02 16:16:28 -0700184 unsigned size = 2U << (fpType - CborHalfFloatType);
185 if (size == 8)
186 put64(buf + 1, *(const uint64_t*)value);
187 else if (size == 4)
188 put32(buf + 1, *(const uint32_t*)value);
189 else
190 put16(buf + 1, *(const uint16_t*)value);
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700191 ++encoder->added;
Thiago Macieira81f33432015-07-02 16:16:28 -0700192 return append_to_buffer(encoder, buf, size + 1);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700193}
Thiago Macieira8f98a112015-05-08 17:25:29 -0700194
195CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
196{
Thiago Macieiradbc01292016-06-06 17:02:25 -0700197 /* tags don't count towards the number of elements in an array or map */
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700198 return encode_number_no_update(encoder, tag, TagType << MajorTypeShift);
Thiago Macieira8f98a112015-05-08 17:25:29 -0700199}
Thiago Macieirab54debe2015-05-08 17:42:39 -0700200
Thiago Macieira5752ce52015-06-16 12:10:03 -0700201static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700202{
203 CborError err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700204 if (err && !isOomError(err))
Thiago Macieirab54debe2015-05-08 17:42:39 -0700205 return err;
206 return append_to_buffer(encoder, string, length);
207}
208
Thiago Macieira5752ce52015-06-16 12:10:03 -0700209CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700210{
211 return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
212}
213
214CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
215{
216 return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
217}
Thiago Macieira355817e2015-05-08 18:38:18 -0700218
Thiago Macieira56635242015-09-20 17:44:51 -0700219#ifdef __GNUC__
220__attribute__((noinline))
221#endif
222static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
Thiago Macieira355817e2015-05-08 18:38:18 -0700223{
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900224 CborError err;
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700225 container->ptr = encoder->ptr;
226 container->end = encoder->end;
227 ++encoder->added;
228 container->added = 0;
229
230 cbor_static_assert(((MapType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == CborIteratorFlag_ContainerIsMap);
231 cbor_static_assert(((ArrayType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == 0);
232 container->flags = shiftedMajorType & CborIteratorFlag_ContainerIsMap;
233
234 if (length == CborIndefiniteLength) {
235 container->flags |= CborIteratorFlag_UnknownLength;
236 err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength);
237 } else {
238 err = encode_number_no_update(container, length, shiftedMajorType);
239 }
Erich Keane47a78562015-08-10 15:19:50 -0700240 if (err && !isOomError(err))
Thiago Macieira355817e2015-05-08 18:38:18 -0700241 return err;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900242
Thiago Macieira355817e2015-05-08 18:38:18 -0700243 return CborNoError;
244}
245
246CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
247{
Thiago Macieira56635242015-09-20 17:44:51 -0700248 return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700249}
250
251CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
252{
Thiago Macieira4e9626c2015-09-21 14:57:17 -0700253 if (length != CborIndefiniteLength && length > SIZE_MAX / 2)
254 return CborErrorDataTooLarge;
Thiago Macieira56635242015-09-20 17:44:51 -0700255 return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700256}
257
258CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
259{
Thiago Macieira397f9792015-09-18 19:36:26 -0700260 if (encoder->end)
261 encoder->ptr = containerEncoder->ptr;
262 else
263 encoder->bytes_needed = containerEncoder->bytes_needed;
Erich Keane47a78562015-08-10 15:19:50 -0700264 encoder->end = containerEncoder->end;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900265 if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
266 return append_byte_to_buffer(encoder, BreakByte);
Thiago Macieira355817e2015-05-08 18:38:18 -0700267 return CborNoError;
268}