blob: 3b9874f5f3293353df191476cc08dca433cf0ebb [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;
40 encoder->flags = flags;
41}
42
Thiago Macieira5752ce52015-06-16 12:10:03 -070043static inline void put16(void *where, uint16_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070044{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070045 v = cbor_htons(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070046 memcpy(where, &v, sizeof(v));
47}
48
Erich Keane47a78562015-08-10 15:19:50 -070049// Note: Since this is currently only used in situations where OOM is the only
50// valid error, we KNOW this to be true. Thus, this function now returns just 'true',
51// but if in the future, any function starts returning a non-OOM error, this will need
52// to be changed to the test. At the moment, this is done to prevent more branches
53// being created in the tinycbor output
54static inline bool isOomError(CborError err)
55{
56 (void) err;
57 return true;
58}
59
Thiago Macieira5752ce52015-06-16 12:10:03 -070060static inline void put32(void *where, uint32_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070061{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070062 v = cbor_htonl(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070063 memcpy(where, &v, sizeof(v));
64}
65
Thiago Macieira5752ce52015-06-16 12:10:03 -070066static inline void put64(void *where, uint64_t v)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070067{
Thiago Macieira5934a9f2015-06-16 11:55:28 -070068 v = cbor_htonll(v);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070069 memcpy(where, &v, sizeof(v));
70}
71
Thiago Macieira397f9792015-09-18 19:36:26 -070072static inline bool would_overflow(CborEncoder *encoder, size_t len)
73{
74 ptrdiff_t remaining = (ptrdiff_t)encoder->end;
75 remaining -= remaining ? (ptrdiff_t)encoder->ptr : encoder->bytes_needed;
76 remaining -= (ptrdiff_t)len;
77 return unlikely(remaining < 0);
78}
79
80static inline void advance_ptr(CborEncoder *encoder, size_t n)
81{
82 if (encoder->end)
83 encoder->ptr += n;
84 else
85 encoder->bytes_needed += n;
86}
87
Thiago Macieira5752ce52015-06-16 12:10:03 -070088static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070089{
Thiago Macieira397f9792015-09-18 19:36:26 -070090 if (would_overflow(encoder, len)) {
Erich Keane47a78562015-08-10 15:19:50 -070091 if (encoder->end != NULL) {
92 len -= encoder->end - encoder->ptr;
Thiago Macieira397f9792015-09-18 19:36:26 -070093 encoder->end = NULL;
94 encoder->bytes_needed = 0;
Erich Keane47a78562015-08-10 15:19:50 -070095 }
96
Thiago Macieira397f9792015-09-18 19:36:26 -070097 advance_ptr(encoder, len);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070098 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -070099 }
100
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700101 memcpy(encoder->ptr, data, len);
102 encoder->ptr += len;
103 return CborNoError;
104}
105
Thiago Macieira5752ce52015-06-16 12:10:03 -0700106static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte)
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900107{
Thiago Macieira397f9792015-09-18 19:36:26 -0700108 if (would_overflow(encoder, 1)) {
Erich Keane47a78562015-08-10 15:19:50 -0700109 if (encoder->end != NULL) {
Thiago Macieira397f9792015-09-18 19:36:26 -0700110 encoder->end = NULL;
111 encoder->bytes_needed = 0;
Erich Keane47a78562015-08-10 15:19:50 -0700112 }
113
Thiago Macieira397f9792015-09-18 19:36:26 -0700114 advance_ptr(encoder, 1);
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900115 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -0700116 }
117
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900118 *encoder->ptr++ = byte;
119 return CborNoError;
120}
121
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700122static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
123{
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700124 /* Little-endian would have been so much more convenient here:
125 * We could just write at the beginning of buf but append_to_buffer
126 * only the necessary bytes.
127 * Since it has to be big endian, do it the other way around:
128 * write from the end. */
Thiago Macieira5752ce52015-06-16 12:10:03 -0700129 uint8_t buf[1 + sizeof(ui)];
130 uint8_t *const bufend = buf + sizeof(buf);
131 uint8_t *bufstart = bufend - 1;
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700132 put64(buf + 1, ui); // we probably have a bunch of zeros in the beginning
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700133
134 if (ui < Value8Bit) {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700135 *bufstart += shiftedMajorType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700136 } else {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700137 unsigned more = 0;
Thiago Macieira13c85792015-07-07 16:15:41 -0700138 if (ui > 0xffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700139 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700140 if (ui > 0xffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700141 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700142 if (ui > 0xffffffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700143 ++more;
144 bufstart -= 1 << more;
145 *bufstart = shiftedMajorType + Value8Bit + more;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700146 }
147
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700148 return append_to_buffer(encoder, bufstart, bufend - bufstart);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700149}
150
151CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
152{
153 return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
154}
155
156CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
157{
158 // adapted from code in RFC 7049 appendix C (pseudocode)
159 uint64_t ui = value >> 63; // extend sign to whole length
160 uint8_t majorType = ui & 0x20; // extract major type
161 ui ^= value; // complement negatives
162 return encode_number(encoder, ui, majorType);
163}
164
165CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
166{
167#ifndef CBOR_ENCODER_NO_CHECK_USER
168 // check if this is a valid simple type
169 if (value >= HalfPrecisionFloat && value <= Break)
170 return CborErrorIllegalSimpleType;
171#endif
172 return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
173}
174
Thiago Macieira81f33432015-07-02 16:16:28 -0700175CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700176{
Thiago Macieira81f33432015-07-02 16:16:28 -0700177 uint8_t buf[1 + sizeof(uint64_t)];
178 assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
179 buf[0] = fpType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700180
Thiago Macieira81f33432015-07-02 16:16:28 -0700181 unsigned size = 2U << (fpType - CborHalfFloatType);
182 if (size == 8)
183 put64(buf + 1, *(const uint64_t*)value);
184 else if (size == 4)
185 put32(buf + 1, *(const uint32_t*)value);
186 else
187 put16(buf + 1, *(const uint16_t*)value);
188 return append_to_buffer(encoder, buf, size + 1);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700189}
Thiago Macieira8f98a112015-05-08 17:25:29 -0700190
191CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
192{
193 return encode_number(encoder, tag, TagType << MajorTypeShift);
194}
Thiago Macieirab54debe2015-05-08 17:42:39 -0700195
Thiago Macieira5752ce52015-06-16 12:10:03 -0700196static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700197{
198 CborError err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700199 if (err && !isOomError(err))
Thiago Macieirab54debe2015-05-08 17:42:39 -0700200 return err;
201 return append_to_buffer(encoder, string, length);
202}
203
Thiago Macieira5752ce52015-06-16 12:10:03 -0700204CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700205{
206 return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
207}
208
209CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
210{
211 return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
212}
Thiago Macieira355817e2015-05-08 18:38:18 -0700213
Thiago Macieira56635242015-09-20 17:44:51 -0700214#ifdef __GNUC__
215__attribute__((noinline))
216#endif
217static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
Thiago Macieira355817e2015-05-08 18:38:18 -0700218{
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900219 CborError err;
220 if (length == CborIndefiniteLength)
221 err = append_byte_to_buffer(encoder, shiftedMajorType + IndefiniteLength);
222 else
223 err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700224 if (err && !isOomError(err))
Thiago Macieira355817e2015-05-08 18:38:18 -0700225 return err;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900226
Thiago Macieira355817e2015-05-08 18:38:18 -0700227 *container = *encoder;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900228 container->flags = length == CborIndefiniteLength ? CborIteratorFlag_UnknownLength : 0;
Thiago Macieira355817e2015-05-08 18:38:18 -0700229 return CborNoError;
230}
231
232CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
233{
Thiago Macieira56635242015-09-20 17:44:51 -0700234 return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700235}
236
237CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
238{
Thiago Macieira56635242015-09-20 17:44:51 -0700239 return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift);
Thiago Macieira355817e2015-05-08 18:38:18 -0700240}
241
242CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
243{
Thiago Macieira397f9792015-09-18 19:36:26 -0700244 if (encoder->end)
245 encoder->ptr = containerEncoder->ptr;
246 else
247 encoder->bytes_needed = containerEncoder->bytes_needed;
Erich Keane47a78562015-08-10 15:19:50 -0700248 encoder->end = containerEncoder->end;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900249 if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
250 return append_byte_to_buffer(encoder, BreakByte);
Thiago Macieira355817e2015-05-08 18:38:18 -0700251 return CborNoError;
252}