blob: e2cd4794249116c94c68bc7b6f2ee0f9288b0765 [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>
Thiago Macieira2312efd2015-05-06 16:07:48 -070032#include <stdlib.h>
Thiago Macieira54a0e102015-05-05 21:25:06 -070033#include <string.h>
34
Thiago Macieirac70169f2015-05-06 07:49:44 -070035#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) && \
36 (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
37# pragma GCC optimize("-ffunction-sections")
38#endif
39
Thiago Macieira54a0e102015-05-05 21:25:06 -070040/**
41 * \typedef CborValue
42 * This type contains one value parsed from the CBOR stream.
43 *
44 * To get the actual type, use cbor_value_get_type(). Then extract the value
45 * using one of the corresponding functions: cbor_value_get_boolean(), cbor_value_get_int64(),
46 * cbor_value_get_int(), cbor_value_copy_string(), cbor_value_get_array(), cbor_value_get_map(),
47 * cbor_value_get_double(), cbor_value_get_float().
48 *
49 * In C++ and C11 modes, you can additionally use the cbor_value_get_integer()
50 * and cbor_value_get_floating_point() generic functions.
51 *
52 * \omit
53 * Implementation details: the CborValue contains these fields:
54 * \list
55 * \li ptr: pointer to the actual data
56 * \li flags: flags from the decoder
Thiago Macieira2312efd2015-05-06 16:07:48 -070057 * \li extra: partially decoded integer value (0, 1 or 2 bytes)
Thiago Macieira54a0e102015-05-05 21:25:06 -070058 * \li remaining: remaining items in this collection after this item or UINT32_MAX if length is unknown
59 * \endlist
60 * \endomit
61 */
62
63static bool makeError(CborParser *parser, CborParserError error, uint64_t addend)
64{
65 (void)addend;
66 parser->error = error;
67 return false;
68}
69#ifdef CBOR_PARSER_NO_DETAILED_ERROR
70// This way, the compiler should eliminate all error settings by dead code elimination
Thiago Macieirac70169f2015-05-06 07:49:44 -070071# define makeError(parser, err, addend) makeError(parser, (err) * 0 + CborErrorUnknownError, addend)
Thiago Macieira54a0e102015-05-05 21:25:06 -070072#endif
73
74static inline uint16_t get16(const char *ptr)
75{
76 uint16_t result;
77 memcpy(&result, ptr, sizeof(result));
78 return be16toh(result);
79}
80
81static inline uint32_t get32(const char *ptr)
82{
83 uint32_t result;
84 memcpy(&result, ptr, sizeof(result));
85 return be32toh(result);
86}
87
88static inline uint64_t get64(const char *ptr)
89{
90 uint64_t result;
91 memcpy(&result, ptr, sizeof(result));
92 return be64toh(result);
93}
94
Thiago Macieira2312efd2015-05-06 16:07:48 -070095static inline bool extract_length(CborParser *parser, const char **ptr, size_t *len)
96{
97 uint8_t additional_information = **ptr & SmallValueMask;
98 if (additional_information < Value8Bit) {
99 *len = additional_information;
100 ++(*ptr);
101 return true;
102 }
103 if (unlikely(additional_information > Value64Bit))
104 return makeError(parser, CborErrorIllegalNumber, additional_information);
105
106 size_t bytesNeeded = 1 << (additional_information - Value8Bit);
107 if (unlikely(*ptr + 1 + bytesNeeded > parser->end)) {
108 return makeError(parser, CborErrorUnexpectedEOF, 0);
109 } else if (bytesNeeded == 1) {
110 *len = (*ptr)[1];
111 } else if (bytesNeeded == 2) {
112 *len = get16(*ptr + 1);
113 } else if (bytesNeeded == 4) {
114 *len = get32(*ptr + 1);
115 } else {
116 uint64_t v = get64(*ptr + 1);
117 *len = v;
118 if (v != *len)
119 return makeError(parser, CborErrorDataTooLarge, 0);
120 }
121 *ptr += bytesNeeded;
122 return true;
123}
124
Thiago Macieira54a0e102015-05-05 21:25:06 -0700125static void preparse_value(CborValue *it)
126{
127 // are we at the end?
128 CborParser *parser = it->parser;
129 if (it->ptr == parser->end)
130 goto error_eof;
131
132 uint8_t descriptor = *it->ptr;
133 it->type = descriptor & MajorTypeMask;
134 it->flags = 0;
135 descriptor &= SmallValueMask;
136 it->extra = descriptor;
137
Thiago Macieirac70169f2015-05-06 07:49:44 -0700138 size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit));
139 if (it->ptr + 1 + bytesNeeded > parser->end)
140 goto error_eof;
141
Thiago Macieira54a0e102015-05-05 21:25:06 -0700142 switch ((CborMajorTypes)(it->type >> MajorTypeShift)) {
143 case NegativeIntegerType:
144 it->flags |= CborIteratorFlag_NegativeInteger;
145 // fall through
146 case UnsignedIntegerType:
147 case TagType:
148 break;
149
150 case SimpleTypesType:
151 switch (descriptor) {
152 case FalseValue:
153 it->extra = false;
154 // fall through
155 case TrueValue:
156 case NullValue:
157 case UndefinedValue:
158 case HalfPrecisionFloat:
159 case SinglePrecisionFloat:
160 case DoublePrecisionFloat:
Thiago Macieirac70169f2015-05-06 07:49:44 -0700161 it->type = *it->ptr;
Thiago Macieira54a0e102015-05-05 21:25:06 -0700162 break;
163
164 case SimpleTypeInNextByte:
Thiago Macieira54a0e102015-05-05 21:25:06 -0700165 it->extra = it->ptr[1];
166#ifndef CBOR_PARSER_NO_STRICT_CHECKS
167 if (it->extra < 32) {
168 makeError(parser, CborErrorIllegalSimpleType, it->extra);
169 goto error;
170 }
171#endif
172 case 28:
173 case 29:
174 case 30:
175 makeError(parser, CborErrorUnknownType, *it->ptr);
176 goto error;
177
178 case Break:
179 makeError(parser, CborErrorUnexpectedBreak, 0);
180 goto error;
181 }
182 break;
183
184 case ByteStringType:
185 case TextStringType:
186 case ArrayType:
187 case MapType:
188 if (descriptor == IndefiniteLength)
189 it->flags |= CborIteratorFlag_UnknownLength;
190 break;
191 }
192
193 // try to decode up to 16 bits
194 if (descriptor < Value8Bit)
195 return;
196 if (unlikely(descriptor > Value64Bit))
197 goto illegal_number_error;
198
Thiago Macieira54a0e102015-05-05 21:25:06 -0700199 if (descriptor == Value8Bit) {
200 it->extra = it->ptr[1];
201 return;
202 }
203 if (descriptor == Value16Bit) {
204 it->extra = get16(it->ptr + 1);
205 return;
206 }
207 // Value32Bit or Value64Bit
208 it->flags |= CborIteratorFlag_IntegerValueTooLarge;
209 return;
210
211illegal_number_error:
212 makeError(parser, CborErrorIllegalNumber, it->ptr[1]);
213 goto error;
214
215error_eof:
216 makeError(parser, CborErrorUnexpectedEOF, 0);
217error:
218 it->type = CborInvalidType;
219 return;
220}
221
Thiago Macieira2312efd2015-05-06 16:07:48 -0700222/** \internal
223 *
224 * Decodes the CBOR integer value when it is larger than the 16 bits available
225 * in value->extra. This function requires that value->flags have the
226 * CborIteratorFlag_IntegerValueTooLarge flag set.
227 *
228 * This function is also used to extract single- and double-precision floating
229 * point values (SinglePrecisionFloat == Value32Bit and DoublePrecisionFloat ==
230 * Value64Bit).
231 */
Thiago Macieira54a0e102015-05-05 21:25:06 -0700232uint64_t _cbor_value_decode_int64_internal(const CborValue *value)
233{
Thiago Macieira2312efd2015-05-06 16:07:48 -0700234 assert(value->flags & CborIteratorFlag_IntegerValueTooLarge ||
235 value->type == CborFloatType || value->type == CborDoubleType);
Thiago Macieira54a0e102015-05-05 21:25:06 -0700236 if ((*value->ptr & SmallValueMask) == Value32Bit)
237 return get32(value->ptr + 1);
238
239 assert((*value->ptr & SmallValueMask) == Value64Bit);
240 return get64(value->ptr + 1);
241}
242
243/**
244 * Initializes the CBOR parser for parsing \a size bytes beginning at \a
245 * buffer. Parsing will use flags set in \a flags. The iterator to the first
246 * element is returned in \a it.
Thiago Macieira2312efd2015-05-06 16:07:48 -0700247 *
248 * The \a parser structure needs to remain valid throughout the decoding
249 * process. It is not thread-safe to share one CborParser among multiple
250 * threads iterating at the same time, but the object can be copied so multiple
251 * threads can iterate.
252 *
253 * ### Write how to determine the end pointer
254 * ### Write how to do limited-buffer windowed decoding
Thiago Macieira54a0e102015-05-05 21:25:06 -0700255 */
256void cbor_parser_init(const char *buffer, size_t size, int flags, CborParser *parser, CborValue *it)
257{
258 memset(parser, 0, sizeof(*parser));
259 parser->end = buffer + size;
260 parser->error = CborNoError;
261 parser->flags = flags;
262 it->parser = parser;
263 it->ptr = buffer;
264 it->remaining = 1; // there's one type altogether, usually an array or map
265 preparse_value(it);
266}
267
Thiago Macieira54a0e102015-05-05 21:25:06 -0700268static bool advance_internal(CborValue *it)
269{
270 unsigned size = 1 << (*it->ptr - Value8Bit);
271 if (it->ptr + size > it->parser->end)
272 return makeError(it->parser, CborErrorUnexpectedEOF, 0);
273
274 it->ptr += size;
275 if (it->remaining == UINT32_MAX && *it->ptr == (char)BreakByte) {
276 // end of map or array
Thiago Macieira54a0e102015-05-05 21:25:06 -0700277 it->remaining = 0;
278 return true;
279 }
280
281 if (it->remaining != UINT32_MAX)
282 --it->remaining;
283 preparse_value(it);
284 return true;
285}
286
Thiago Macieira2312efd2015-05-06 16:07:48 -0700287static bool is_fixed_type(uint8_t type)
288{
289 return type != CborTextStringType && type != CborByteStringType && type != CborArrayType &&
290 type != CborMapType;
291}
292
293/**
294 * Advances the CBOR value \a it by one fixed-size position. Fixed-size types
295 * are: integers, tags, simple types (including boolean, null and undefined
296 * values) and floating point types. This function returns true if the
297 * advancing succeeded, false on a decoding error or if there are no more
298 * items.
299 *
300 * \sa cbor_value_at_end(), cbor_value_advance(), cbor_value_begin_recurse(), cbor_value_end_recurse()
301 */
Thiago Macieira54a0e102015-05-05 21:25:06 -0700302bool cbor_value_advance_fixed(CborValue *it)
303{
Thiago Macieira2312efd2015-05-06 16:07:48 -0700304 assert(it->type != CborInvalidType);
305 assert(!is_fixed_type(it->type));
Thiago Macieira54a0e102015-05-05 21:25:06 -0700306 return it->remaining && advance_internal(it);
307}
308
Thiago Macieira2312efd2015-05-06 16:07:48 -0700309/**
310 * Advances the CBOR value \a it by one element, skipping over containers.
311 * Unlike cbor_value_advance_fixed(), this function can be called on a CBOR
312 * value of any type. However, if the type is a container (map or array) or a
313 * string with a chunked payload, this function will not run in constant time
314 * and will recurse into itself (it will run on O(n) time for the number of
315 * elements or chunks and will use O(n) memory for the number of nested
316 * containers).
317 *
318 * This function returns true if it advanced by one element, false if this was
319 * the last element or a decoding error happened.
320 *
321 * \sa cbor_value_at_end(), cbor_value_advance_fixed(), cbor_value_begin_recurse(), cbor_value_end_recurse()
322 */
323bool cbor_value_advance(CborValue *it)
324{
325 assert(it->type != CborInvalidType);
326 if (!it->remaining)
327 return false;
328 if (is_fixed_type(it->type))
329 return cbor_value_advance_fixed(it);
330
331 if (cbor_value_is_container(it)) {
332 // map or array
333 CborValue recursed;
334 if (!cbor_value_enter_container(it, &recursed))
335 return false;
336 while (!cbor_value_at_end(&recursed))
337 if (!cbor_value_advance(&recursed))
338 return false;
339 if (!cbor_value_leave_container(it, &recursed))
340 return false;
341 } else {
342 // string
343 if (!cbor_value_copy_string(it, NULL, 0, it))
344 return false;
345 }
346 return true;
347}
348
349/**
350 * Returns true if the \a it value is a container and requires recursion in
351 * order to decode (maps and arrays), false otherwise.
352 */
353bool cbor_value_is_container(const CborValue *it)
Thiago Macieira54a0e102015-05-05 21:25:06 -0700354{
355 return it->type == CborArrayType || it->type == CborMapType;
356}
357
Thiago Macieira2312efd2015-05-06 16:07:48 -0700358/**
359 * Creates a CborValue iterator pointing to the first element of the container
360 * represented by \a it and saves it in \a recursed. The \a it container object
361 * needs to be kept and passed again to cbor_value_leave_container() in order
362 * to continue iterating past this container.
363 *
364 * \sa cbor_value_is_container(), cbor_value_leave_container(), cbor_value_advance()
365 */
366bool cbor_value_enter_container(const CborValue *it, CborValue *recursed)
Thiago Macieira54a0e102015-05-05 21:25:06 -0700367{
Thiago Macieira2312efd2015-05-06 16:07:48 -0700368 assert(cbor_value_is_container(it));
Thiago Macieira54a0e102015-05-05 21:25:06 -0700369 *recursed = *it;
370 if (it->flags & CborIteratorFlag_UnknownLength) {
371 recursed->remaining = UINT32_MAX;
372 } else {
373 uint64_t len = _cbor_value_extract_int64_helper(it);
374 recursed->remaining = len;
375 if (recursed->remaining != len || len == UINT32_MAX)
376 return makeError(it->parser, CborErrorDataTooLarge, len);
377 }
Thiago Macieirac70169f2015-05-06 07:49:44 -0700378 return advance_internal(recursed);
379}
380
Thiago Macieira2312efd2015-05-06 16:07:48 -0700381/**
382 * Updates \a it to point to the next element after the container. The \a
383 * recursed object needs to point to the last element of the container.
384 *
385 * \sa cbor_value_enter_container(), cbor_value_at_end()
386 */
387bool cbor_value_leave_container(CborValue *it, const CborValue *recursed)
Thiago Macieirac70169f2015-05-06 07:49:44 -0700388{
Thiago Macieira2312efd2015-05-06 16:07:48 -0700389 assert(cbor_value_is_container(it));
Thiago Macieirac70169f2015-05-06 07:49:44 -0700390 assert(cbor_value_at_end(recursed));
391 it->ptr = recursed->ptr;
392 return advance_internal(it);
393}
394
Thiago Macieira2312efd2015-05-06 16:07:48 -0700395static bool copy_chunk(CborParser *parser, uint8_t expectedType, char *dst, const char **src, size_t *chunkLen)
Thiago Macieirac70169f2015-05-06 07:49:44 -0700396{
Thiago Macieira2312efd2015-05-06 16:07:48 -0700397 // is this the right type?
398 if ((**src & MajorTypeMask) != expectedType)
399 return makeError(parser, CborErrorIllegalType, **src);
400
401 if (!extract_length(parser, src, chunkLen))
402 return false; // error condition already set
403
404 if (dst)
405 memcpy(dst, *src, *chunkLen);
406
407 *src += *chunkLen;
408 return true;
409}
410
411/**
412 * Calculates the length of the string in \a value and stores the result in \a
413 * len. This function is different from cbor_value_get_string_length() in that
414 * it calculates the length even for strings sent in chunks. For that reason,
415 * this function may not run in constant time (it will run in O(n) time on the
416 * number of chunks).
417 *
418 * This function returns true if the length was successfully calculated or false
419 * if there was a decoding error.
420 *
421 * \note On 32-bit platforms, this function will return false and set condition
422 * of \ref CborErrorDataTooLarge if the stream indicates a length that is too
423 * big to fit in 32-bit.
424 *
425 * \sa cbor_value_get_string_length(), cbor_value_copy_string(), cbor_value_is_length_known()
426 */
427bool cbor_value_calculate_string_length(const CborValue *value, size_t *len)
428{
Thiago Macieirac70169f2015-05-06 07:49:44 -0700429 if (cbor_value_get_string_length(value, len))
430 return true;
431
432 // chunked string, iterate to calculate full size
433 size_t total = 0;
Thiago Macieira2312efd2015-05-06 16:07:48 -0700434 const char *ptr = value->ptr + 1;
435 while (true) {
436 if (ptr == value->parser->end)
437 return makeError(value->parser, CborErrorUnexpectedEOF, 0);
438
439 if (*ptr == (char)BreakByte) {
440 ++ptr;
441 break;
442 }
443
444 size_t chunkLen;
445 if (!copy_chunk(value->parser, value->type, NULL, &ptr, &chunkLen))
446 return false;
447
448 if (!add_check_overflow(total, chunkLen, &total))
449 return makeError(value->parser, CborErrorDataTooLarge, 0);
450 }
451
452 *len = total;
453 return true;
Thiago Macieirac70169f2015-05-06 07:49:44 -0700454}
455
Thiago Macieira2312efd2015-05-06 16:07:48 -0700456/**
457 * Allocates memory for the string pointed by \a value and copies it into this
458 * buffer. The pointer to the buffer is stored in \a buffer and the number of
459 * bytes copied is stored in \a len (those variables must not be NULL).
460 *
461 * This function returns false if a decoding error occurred or if we ran out of
462 * memory. OOM situations are indicated by setting both \c{*buffer} to \c NULL.
463 * If the caller needs to recover from an OOM condition, it should initialize
464 * the variable to a non-NULL value (it does not have to be a valid pointer).
465 *
466 * On success, this function returns true and \c{*buffer} will contain a valid
467 * pointer that must be freed by calling \c{free()}. This is the case even for
468 * zero-length strings.
469 *
470 * The \a next pointer, if not null, will be updated to point to the next item
471 * after this string.
472 *
473 * \note This function does not perform UTF-8 validation on the incoming text
474 * string.
475 *
476 * \sa cbor_value_copy_string()
477 */
478bool cbor_value_dup_string(const CborValue *value, char **buffer, size_t *len, CborValue *next)
Thiago Macieirac70169f2015-05-06 07:49:44 -0700479{
Thiago Macieira2312efd2015-05-06 16:07:48 -0700480 assert(buffer);
481 assert(len);
482 if (!cbor_value_calculate_string_length(value, len))
483 return false;
Thiago Macieirac70169f2015-05-06 07:49:44 -0700484
Thiago Macieira2312efd2015-05-06 16:07:48 -0700485 *buffer = malloc(*len + 1);
486 if (!*buffer) {
Thiago Macieirac70169f2015-05-06 07:49:44 -0700487 // out of memory
Thiago Macieira2312efd2015-05-06 16:07:48 -0700488 return false;
Thiago Macieirac70169f2015-05-06 07:49:44 -0700489 }
Thiago Macieira2312efd2015-05-06 16:07:48 -0700490 size_t copied = cbor_value_copy_string(value, *buffer, *len + 1, next);
Thiago Macieirac70169f2015-05-06 07:49:44 -0700491 if (copied == SIZE_MAX) {
Thiago Macieira2312efd2015-05-06 16:07:48 -0700492 free(*buffer);
493 return false;
Thiago Macieirac70169f2015-05-06 07:49:44 -0700494 }
495 assert(copied == *len);
Thiago Macieira2312efd2015-05-06 16:07:48 -0700496 return true;
497}
498
499/**
500 * Copies the string pointed by \a value into the buffer provided at \a buffer
501 * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not
502 * copy anything and will only update the \a next value.
503 *
504 * This function returns \c SIZE_MAX if a decoding error occurred or if the
505 * buffer was not large enough. If you need to calculate the length of the
506 * string in order to preallocate a buffer, use
507 * cbor_value_calculate_string_length().
508 *
509 * On success, this function returns the number of bytes copied. If the buffer
510 * is large enough, this function will insert a null byte after the last copied
511 * byte, to facilitate manipulation of text strings. That byte is not included
512 * in the returned value.
513 *
514 * The \a next pointer, if not null, will be updated to point to the next item
515 * after this string. If \a value points to the last item, then \a next will be
516 * invalid.
517 *
518 * \note This function does not perform UTF-8 validation on the incoming text
519 * string.
520 *
521 * \sa cbor_value_dup_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length()
522 */
523size_t cbor_value_copy_string(const CborValue *value, char *buffer,
524 size_t buflen, CborValue *next)
525{
526 assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value));
527
528 size_t total;
529 const char *ptr = value->ptr;
530 if (cbor_value_is_length_known(value)) {
531 // easy case: fixed length
532 if (!extract_length(value->parser, &ptr, &total))
533 return SIZE_MAX;
534 if (buffer) {
535 if (buflen < total)
536 return SIZE_MAX;
537 memcpy(buffer, ptr, total);
538 ptr += total;
539 }
540 } else {
541 // chunked
542 ++ptr;
543 while (true) {
544 if (ptr == value->parser->end)
545 return makeError(value->parser, CborErrorUnexpectedEOF, 0);
546
547 if (*ptr == (char)BreakByte) {
548 ++ptr;
549 break;
550 }
551
552 // is this the right type?
553 if ((*ptr & MajorTypeMask) != value->type)
554 return makeError(value->parser, CborErrorIllegalType, *ptr);
555
556 size_t chunkLen;
557 if (!extract_length(value->parser, &ptr, &chunkLen))
558 return false; // error condition already set
559
560 size_t newTotal;
561 if (unlikely(!add_check_overflow(total, chunkLen, &newTotal)))
562 return makeError(value->parser, CborErrorDataTooLarge, 0);
563
564 if (buffer) {
565 if (buflen < newTotal)
566 return SIZE_MAX;
567 memcpy(buffer + total, ptr, chunkLen);
568 }
569 ptr += chunkLen;
570 total = newTotal;
571 }
572 }
573
574 // is there enough room for the ending NUL byte?
575 if (buffer && buflen > total)
576 buffer[total] = '\0';
577
578 if (next) {
579 *next = *value;
580 next->ptr = ptr;
581 if (!next->remaining) {
582 next->type = CborInvalidType;
583 } else {
584 if (next->remaining != UINT32_MAX)
585 --next->remaining;
586 preparse_value(next);
587 }
588 }
589 return total;
Thiago Macieirac70169f2015-05-06 07:49:44 -0700590}
591
592bool cbor_value_get_half_float(const CborValue *value, void *result)
593{
594 if (value->type != CborHalfFloatType)
595 return false;
596
597 // size has been computed already
598 uint16_t v = get16(value->ptr + 1);
599 memcpy(result, &v, sizeof(v));
Thiago Macieira54a0e102015-05-05 21:25:06 -0700600 return true;
601}