blob: 941fbe6c4abceeb94825cbc7d55df89fc22ae26e [file] [log] [blame]
Thiago Macieira54a0e102015-05-05 21:25:06 -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
25#define _BSD_SOURCE
26#include "cbor.h"
27#include "cborconstants_p.h"
28#include "compilersupport_p.h"
29
30#include <assert.h>
31#include <endian.h>
32#include <string.h>
33
Thiago Macieirac70169f2015-05-06 07:49:44 -070034#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) && \
35 (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
36# pragma GCC optimize("-ffunction-sections")
37#endif
38
Thiago Macieira54a0e102015-05-05 21:25:06 -070039/**
40 * \typedef CborValue
41 * This type contains one value parsed from the CBOR stream.
42 *
43 * To get the actual type, use cbor_value_get_type(). Then extract the value
44 * using one of the corresponding functions: cbor_value_get_boolean(), cbor_value_get_int64(),
45 * cbor_value_get_int(), cbor_value_copy_string(), cbor_value_get_array(), cbor_value_get_map(),
46 * cbor_value_get_double(), cbor_value_get_float().
47 *
48 * In C++ and C11 modes, you can additionally use the cbor_value_get_integer()
49 * and cbor_value_get_floating_point() generic functions.
50 *
51 * \omit
52 * Implementation details: the CborValue contains these fields:
53 * \list
54 * \li ptr: pointer to the actual data
55 * \li flags: flags from the decoder
56 * \li extra: partially decoded integer value
57 * \li remaining: remaining items in this collection after this item or UINT32_MAX if length is unknown
58 * \endlist
59 * \endomit
60 */
61
62static bool makeError(CborParser *parser, CborParserError error, uint64_t addend)
63{
64 (void)addend;
65 parser->error = error;
66 return false;
67}
68#ifdef CBOR_PARSER_NO_DETAILED_ERROR
69// This way, the compiler should eliminate all error settings by dead code elimination
Thiago Macieirac70169f2015-05-06 07:49:44 -070070# define makeError(parser, err, addend) makeError(parser, (err) * 0 + CborErrorUnknownError, addend)
Thiago Macieira54a0e102015-05-05 21:25:06 -070071#endif
72
73static inline uint16_t get16(const char *ptr)
74{
75 uint16_t result;
76 memcpy(&result, ptr, sizeof(result));
77 return be16toh(result);
78}
79
80static inline uint32_t get32(const char *ptr)
81{
82 uint32_t result;
83 memcpy(&result, ptr, sizeof(result));
84 return be32toh(result);
85}
86
87static inline uint64_t get64(const char *ptr)
88{
89 uint64_t result;
90 memcpy(&result, ptr, sizeof(result));
91 return be64toh(result);
92}
93
94static void preparse_value(CborValue *it)
95{
96 // are we at the end?
97 CborParser *parser = it->parser;
98 if (it->ptr == parser->end)
99 goto error_eof;
100
101 uint8_t descriptor = *it->ptr;
102 it->type = descriptor & MajorTypeMask;
103 it->flags = 0;
104 descriptor &= SmallValueMask;
105 it->extra = descriptor;
106
Thiago Macieirac70169f2015-05-06 07:49:44 -0700107 size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit));
108 if (it->ptr + 1 + bytesNeeded > parser->end)
109 goto error_eof;
110
Thiago Macieira54a0e102015-05-05 21:25:06 -0700111 switch ((CborMajorTypes)(it->type >> MajorTypeShift)) {
112 case NegativeIntegerType:
113 it->flags |= CborIteratorFlag_NegativeInteger;
114 // fall through
115 case UnsignedIntegerType:
116 case TagType:
117 break;
118
119 case SimpleTypesType:
120 switch (descriptor) {
121 case FalseValue:
122 it->extra = false;
123 // fall through
124 case TrueValue:
125 case NullValue:
126 case UndefinedValue:
127 case HalfPrecisionFloat:
128 case SinglePrecisionFloat:
129 case DoublePrecisionFloat:
Thiago Macieirac70169f2015-05-06 07:49:44 -0700130 it->type = *it->ptr;
Thiago Macieira54a0e102015-05-05 21:25:06 -0700131 break;
132
133 case SimpleTypeInNextByte:
Thiago Macieira54a0e102015-05-05 21:25:06 -0700134 it->extra = it->ptr[1];
135#ifndef CBOR_PARSER_NO_STRICT_CHECKS
136 if (it->extra < 32) {
137 makeError(parser, CborErrorIllegalSimpleType, it->extra);
138 goto error;
139 }
140#endif
141 case 28:
142 case 29:
143 case 30:
144 makeError(parser, CborErrorUnknownType, *it->ptr);
145 goto error;
146
147 case Break:
148 makeError(parser, CborErrorUnexpectedBreak, 0);
149 goto error;
150 }
151 break;
152
153 case ByteStringType:
154 case TextStringType:
155 case ArrayType:
156 case MapType:
157 if (descriptor == IndefiniteLength)
158 it->flags |= CborIteratorFlag_UnknownLength;
159 break;
160 }
161
162 // try to decode up to 16 bits
163 if (descriptor < Value8Bit)
164 return;
165 if (unlikely(descriptor > Value64Bit))
166 goto illegal_number_error;
167
Thiago Macieira54a0e102015-05-05 21:25:06 -0700168 if (descriptor == Value8Bit) {
169 it->extra = it->ptr[1];
170 return;
171 }
172 if (descriptor == Value16Bit) {
173 it->extra = get16(it->ptr + 1);
174 return;
175 }
176 // Value32Bit or Value64Bit
177 it->flags |= CborIteratorFlag_IntegerValueTooLarge;
178 return;
179
180illegal_number_error:
181 makeError(parser, CborErrorIllegalNumber, it->ptr[1]);
182 goto error;
183
184error_eof:
185 makeError(parser, CborErrorUnexpectedEOF, 0);
186error:
187 it->type = CborInvalidType;
188 return;
189}
190
191uint64_t _cbor_value_decode_int64_internal(const CborValue *value)
192{
193 assert(value->flags & CborIteratorFlag_IntegerValueTooLarge);
194 if ((*value->ptr & SmallValueMask) == Value32Bit)
195 return get32(value->ptr + 1);
196
197 assert((*value->ptr & SmallValueMask) == Value64Bit);
198 return get64(value->ptr + 1);
199}
200
201/**
202 * Initializes the CBOR parser for parsing \a size bytes beginning at \a
203 * buffer. Parsing will use flags set in \a flags. The iterator to the first
204 * element is returned in \a it.
205 */
206void cbor_parser_init(const char *buffer, size_t size, int flags, CborParser *parser, CborValue *it)
207{
208 memset(parser, 0, sizeof(*parser));
209 parser->end = buffer + size;
210 parser->error = CborNoError;
211 parser->flags = flags;
212 it->parser = parser;
213 it->ptr = buffer;
214 it->remaining = 1; // there's one type altogether, usually an array or map
215 preparse_value(it);
216}
217
Thiago Macieirac70169f2015-05-06 07:49:44 -0700218#ifndef NDEBUG
Thiago Macieira54a0e102015-05-05 21:25:06 -0700219static bool is_fixed_type(uint8_t type)
220{
221 return type == CborIntegerType || type == CborTagType || type == CborSimpleType;
222}
Thiago Macieirac70169f2015-05-06 07:49:44 -0700223#endif
Thiago Macieira54a0e102015-05-05 21:25:06 -0700224
225static bool advance_internal(CborValue *it)
226{
227 unsigned size = 1 << (*it->ptr - Value8Bit);
228 if (it->ptr + size > it->parser->end)
229 return makeError(it->parser, CborErrorUnexpectedEOF, 0);
230
231 it->ptr += size;
232 if (it->remaining == UINT32_MAX && *it->ptr == (char)BreakByte) {
233 // end of map or array
Thiago Macieira54a0e102015-05-05 21:25:06 -0700234 it->remaining = 0;
235 return true;
236 }
237
238 if (it->remaining != UINT32_MAX)
239 --it->remaining;
240 preparse_value(it);
241 return true;
242}
243
244bool cbor_value_advance_fixed(CborValue *it)
245{
246 assert(is_fixed_type(it->type));
247 return it->remaining && advance_internal(it);
248}
249
250bool cbor_value_is_recursive(const CborValue *it)
251{
252 return it->type == CborArrayType || it->type == CborMapType;
253}
254
255bool cbor_value_begin_recurse(const CborValue *it, CborValue *recursed)
256{
257 assert(cbor_value_is_recursive(it));
258 *recursed = *it;
259 if (it->flags & CborIteratorFlag_UnknownLength) {
260 recursed->remaining = UINT32_MAX;
261 } else {
262 uint64_t len = _cbor_value_extract_int64_helper(it);
263 recursed->remaining = len;
264 if (recursed->remaining != len || len == UINT32_MAX)
265 return makeError(it->parser, CborErrorDataTooLarge, len);
266 }
Thiago Macieirac70169f2015-05-06 07:49:44 -0700267 return advance_internal(recursed);
268}
269
270bool cbor_value_end_recurse(CborValue *it, const CborValue *recursed)
271{
272 assert(cbor_value_is_recursive(it));
273 assert(cbor_value_at_end(recursed));
274 it->ptr = recursed->ptr;
275 return advance_internal(it);
276}
277
278static bool calculate_string_length(const CborValue *value, size_t *len)
279{
280 if (!cbor_value_is_byte_string(value) && !cbor_value_is_text_string(value))
Thiago Macieira54a0e102015-05-05 21:25:06 -0700281 return false;
Thiago Macieirac70169f2015-05-06 07:49:44 -0700282 if (cbor_value_get_string_length(value, len))
283 return true;
284
285 // chunked string, iterate to calculate full size
286 size_t total = 0;
287 const char *ptr = value->ptr;
288}
289
290size_t cbor_value_dup_string(const CborValue *value, char **buffer, size_t *len, CborValue *next)
291{
292 if (!calculate_string_length(value, len))
293 return SIZE_MAX;
294
295 buffer = malloc(*len + 1);
296 if (!buffer) {
297 // out of memory
298 *len = 0;
299 return NULL;
300 }
301 size_t copied = cbor_value_copy_string(value, buffer, *len + 1, next);
302 if (copied == SIZE_MAX) {
303 free(buffer);
304 return copied;
305 }
306 assert(copied == *len);
307 return copied;
308}
309
310bool cbor_value_get_half_float(const CborValue *value, void *result)
311{
312 if (value->type != CborHalfFloatType)
313 return false;
314
315 // size has been computed already
316 uint16_t v = get16(value->ptr + 1);
317 memcpy(result, &v, sizeof(v));
Thiago Macieira54a0e102015-05-05 21:25:06 -0700318 return true;
319}