blob: ab9c898864eef14ca4e2674e5af655564301a7fc [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 Macieira5752ce52015-06-16 12:10:03 -070072static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070073{
Erich Keane47a78562015-08-10 15:19:50 -070074 if (encoder->end - encoder->ptr - (ptrdiff_t)len < 0) {
75 if (encoder->end != NULL) {
76 len -= encoder->end - encoder->ptr;
77 encoder->end = encoder->ptr = NULL;
78 }
79
80 encoder->ptr += len;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070081 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -070082 }
83
Thiago Macieiraf1cadf02015-05-08 17:19:17 -070084 memcpy(encoder->ptr, data, len);
85 encoder->ptr += len;
86 return CborNoError;
87}
88
Thiago Macieira5752ce52015-06-16 12:10:03 -070089static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte)
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +090090{
Erich Keane47a78562015-08-10 15:19:50 -070091 if (encoder->end <= encoder->ptr) {
92 if (encoder->end != NULL) {
93 encoder->end = encoder->ptr = NULL;
94 }
95
96 ++encoder->ptr;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +090097 return CborErrorOutOfMemory;
Erich Keane47a78562015-08-10 15:19:50 -070098 }
99
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900100 *encoder->ptr++ = byte;
101 return CborNoError;
102}
103
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700104static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
105{
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700106 /* Little-endian would have been so much more convenient here:
107 * We could just write at the beginning of buf but append_to_buffer
108 * only the necessary bytes.
109 * Since it has to be big endian, do it the other way around:
110 * write from the end. */
Thiago Macieira5752ce52015-06-16 12:10:03 -0700111 uint8_t buf[1 + sizeof(ui)];
112 uint8_t *const bufend = buf + sizeof(buf);
113 uint8_t *bufstart = bufend - 1;
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700114 put64(buf + 1, ui); // we probably have a bunch of zeros in the beginning
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700115
116 if (ui < Value8Bit) {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700117 *bufstart += shiftedMajorType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700118 } else {
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700119 unsigned more = 0;
Thiago Macieira13c85792015-07-07 16:15:41 -0700120 if (ui > 0xffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700121 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700122 if (ui > 0xffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700123 ++more;
Thiago Macieira13c85792015-07-07 16:15:41 -0700124 if (ui > 0xffffffffU)
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700125 ++more;
126 bufstart -= 1 << more;
127 *bufstart = shiftedMajorType + Value8Bit + more;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700128 }
129
Thiago Macieiraafa4ff62015-05-09 10:09:55 -0700130 return append_to_buffer(encoder, bufstart, bufend - bufstart);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700131}
132
133CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
134{
135 return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
136}
137
138CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
139{
140 // adapted from code in RFC 7049 appendix C (pseudocode)
141 uint64_t ui = value >> 63; // extend sign to whole length
142 uint8_t majorType = ui & 0x20; // extract major type
143 ui ^= value; // complement negatives
144 return encode_number(encoder, ui, majorType);
145}
146
147CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
148{
149#ifndef CBOR_ENCODER_NO_CHECK_USER
150 // check if this is a valid simple type
151 if (value >= HalfPrecisionFloat && value <= Break)
152 return CborErrorIllegalSimpleType;
153#endif
154 return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
155}
156
Thiago Macieira81f33432015-07-02 16:16:28 -0700157CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700158{
Thiago Macieira81f33432015-07-02 16:16:28 -0700159 uint8_t buf[1 + sizeof(uint64_t)];
160 assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
161 buf[0] = fpType;
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700162
Thiago Macieira81f33432015-07-02 16:16:28 -0700163 unsigned size = 2U << (fpType - CborHalfFloatType);
164 if (size == 8)
165 put64(buf + 1, *(const uint64_t*)value);
166 else if (size == 4)
167 put32(buf + 1, *(const uint32_t*)value);
168 else
169 put16(buf + 1, *(const uint16_t*)value);
170 return append_to_buffer(encoder, buf, size + 1);
Thiago Macieiraf1cadf02015-05-08 17:19:17 -0700171}
Thiago Macieira8f98a112015-05-08 17:25:29 -0700172
173CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
174{
175 return encode_number(encoder, tag, TagType << MajorTypeShift);
176}
Thiago Macieirab54debe2015-05-08 17:42:39 -0700177
Thiago Macieira5752ce52015-06-16 12:10:03 -0700178static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700179{
180 CborError err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700181 if (err && !isOomError(err))
Thiago Macieirab54debe2015-05-08 17:42:39 -0700182 return err;
183 return append_to_buffer(encoder, string, length);
184}
185
Thiago Macieira5752ce52015-06-16 12:10:03 -0700186CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
Thiago Macieirab54debe2015-05-08 17:42:39 -0700187{
188 return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
189}
190
191CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
192{
193 return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
194}
Thiago Macieira355817e2015-05-08 18:38:18 -0700195
196static CborError create_container(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, CborEncoder *container)
197{
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900198 CborError err;
199 if (length == CborIndefiniteLength)
200 err = append_byte_to_buffer(encoder, shiftedMajorType + IndefiniteLength);
201 else
202 err = encode_number(encoder, length, shiftedMajorType);
Erich Keane47a78562015-08-10 15:19:50 -0700203 if (err && !isOomError(err))
Thiago Macieira355817e2015-05-08 18:38:18 -0700204 return err;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900205
Thiago Macieira355817e2015-05-08 18:38:18 -0700206 *container = *encoder;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900207 container->flags = length == CborIndefiniteLength ? CborIteratorFlag_UnknownLength : 0;
Thiago Macieira355817e2015-05-08 18:38:18 -0700208 return CborNoError;
209}
210
211CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
212{
213 return create_container(encoder, length, ArrayType << MajorTypeShift, arrayEncoder);
214}
215
216CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
217{
218 return create_container(encoder, length, MapType << MajorTypeShift, mapEncoder);
219}
220
221CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
222{
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900223 encoder->ptr = containerEncoder->ptr;
Erich Keane47a78562015-08-10 15:19:50 -0700224 encoder->end = containerEncoder->end;
Thiago Macieira8f7bd9e2015-05-12 13:37:46 +0900225 if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
226 return append_byte_to_buffer(encoder, BreakByte);
Thiago Macieira355817e2015-05-08 18:38:18 -0700227 return CborNoError;
228}