1 /****************************************************************************
3 ** Copyright (C) 2015 Intel Corporation
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:
12 ** The above copyright notice and this permission notice shall be included in
13 ** all copies or substantial portions of the Software.
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
23 ****************************************************************************/
27 #include "cborconstants_p.h"
28 #include "compilersupport_p.h"
34 #include "assert_p.h" /* Always include last */
36 void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags)
38 encoder->ptr = buffer;
39 encoder->end = buffer + size;
41 encoder->flags = flags;
44 static inline void put16(void *where, uint16_t v)
47 memcpy(where, &v, sizeof(v));
50 // 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
55 static inline bool isOomError(CborError err)
61 static inline void put32(void *where, uint32_t v)
64 memcpy(where, &v, sizeof(v));
67 static inline void put64(void *where, uint64_t v)
70 memcpy(where, &v, sizeof(v));
73 static inline bool would_overflow(CborEncoder *encoder, size_t len)
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);
81 static inline void advance_ptr(CborEncoder *encoder, size_t n)
86 encoder->bytes_needed += n;
89 static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len)
91 if (would_overflow(encoder, len)) {
92 if (encoder->end != NULL) {
93 len -= encoder->end - encoder->ptr;
95 encoder->bytes_needed = 0;
98 advance_ptr(encoder, len);
99 return CborErrorOutOfMemory;
102 memcpy(encoder->ptr, data, len);
107 static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte)
109 return append_to_buffer(encoder, &byte, 1);
112 static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
114 /* 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. */
120 uint8_t *const bufend = (uint8_t *)buf + sizeof(buf);
121 uint8_t *bufstart = bufend - 1;
122 put64(buf + 1, ui); // we probably have a bunch of zeros in the beginning
124 if (ui < Value8Bit) {
125 *bufstart += shiftedMajorType;
132 if (ui > 0xffffffffU)
134 bufstart -= (size_t)1 << more;
135 *bufstart = shiftedMajorType + Value8Bit + more;
138 return append_to_buffer(encoder, bufstart, bufend - bufstart);
141 static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
144 return encode_number_no_update(encoder, ui, shiftedMajorType);
148 CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
150 return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
153 CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
155 return encode_number(encoder, absolute_value, NegativeIntegerType << MajorTypeShift);
158 CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
160 // 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
164 return encode_number(encoder, ui, majorType);
167 CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
169 #ifndef CBOR_ENCODER_NO_CHECK_USER
170 // check if this is a valid simple type
171 if (value >= HalfPrecisionFloat && value <= Break)
172 return CborErrorIllegalSimpleType;
174 return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
177 CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
179 uint8_t buf[1 + sizeof(uint64_t)];
180 assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
183 unsigned size = 2U << (fpType - CborHalfFloatType);
185 put64(buf + 1, *(const uint64_t*)value);
187 put32(buf + 1, *(const uint32_t*)value);
189 put16(buf + 1, *(const uint16_t*)value);
191 return append_to_buffer(encoder, buf, size + 1);
194 CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
196 // tags don't count towards the number of elements in an array or map
197 return encode_number_no_update(encoder, tag, TagType << MajorTypeShift);
200 static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
202 CborError err = encode_number(encoder, length, shiftedMajorType);
203 if (err && !isOomError(err))
205 return append_to_buffer(encoder, string, length);
208 CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
210 return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
213 CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
215 return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
219 __attribute__((noinline))
221 static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
224 container->ptr = encoder->ptr;
225 container->end = encoder->end;
227 container->added = 0;
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;
233 if (length == CborIndefiniteLength) {
234 container->flags |= CborIteratorFlag_UnknownLength;
235 err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength);
237 err = encode_number_no_update(container, length, shiftedMajorType);
239 if (err && !isOomError(err))
245 CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length)
247 return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift);
250 CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length)
252 if (length != CborIndefiniteLength && length > SIZE_MAX / 2)
253 return CborErrorDataTooLarge;
254 return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift);
257 CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder)
260 encoder->ptr = containerEncoder->ptr;
262 encoder->bytes_needed = containerEncoder->bytes_needed;
263 encoder->end = containerEncoder->end;
264 if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
265 return append_byte_to_buffer(encoder, BreakByte);