1/*
2 * Copyright (c) 2015-2021 Nicholas Fraser and the MPack authors
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the "Software"), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 * the Software, and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22#define MPACK_INTERNAL 1
23
24#include "mpack-common.h"
25
26MPACK_SILENCE_WARNINGS_BEGIN
27
28const char* mpack_error_to_string(mpack_error_t error) {
29 #if MPACK_STRINGS
30 switch (error) {
31 #define MPACK_ERROR_STRING_CASE(e) case e: return #e
32 MPACK_ERROR_STRING_CASE(mpack_ok);
33 MPACK_ERROR_STRING_CASE(mpack_error_io);
34 MPACK_ERROR_STRING_CASE(mpack_error_invalid);
35 MPACK_ERROR_STRING_CASE(mpack_error_unsupported);
36 MPACK_ERROR_STRING_CASE(mpack_error_type);
37 MPACK_ERROR_STRING_CASE(mpack_error_too_big);
38 MPACK_ERROR_STRING_CASE(mpack_error_memory);
39 MPACK_ERROR_STRING_CASE(mpack_error_bug);
40 MPACK_ERROR_STRING_CASE(mpack_error_data);
41 MPACK_ERROR_STRING_CASE(mpack_error_eof);
42 #undef MPACK_ERROR_STRING_CASE
43 }
44 mpack_assert(0, "unrecognized error %i", (int)error);
45 return "(unknown mpack_error_t)";
46 #else
47 MPACK_UNUSED(error);
48 return "";
49 #endif
50}
51
52const char* mpack_type_to_string(mpack_type_t type) {
53 #if MPACK_STRINGS
54 switch (type) {
55 #define MPACK_TYPE_STRING_CASE(e) case e: return #e
56 MPACK_TYPE_STRING_CASE(mpack_type_missing);
57 MPACK_TYPE_STRING_CASE(mpack_type_nil);
58 MPACK_TYPE_STRING_CASE(mpack_type_bool);
59 MPACK_TYPE_STRING_CASE(mpack_type_float);
60 MPACK_TYPE_STRING_CASE(mpack_type_double);
61 MPACK_TYPE_STRING_CASE(mpack_type_int);
62 MPACK_TYPE_STRING_CASE(mpack_type_uint);
63 MPACK_TYPE_STRING_CASE(mpack_type_str);
64 MPACK_TYPE_STRING_CASE(mpack_type_bin);
65 MPACK_TYPE_STRING_CASE(mpack_type_array);
66 MPACK_TYPE_STRING_CASE(mpack_type_map);
67 #if MPACK_EXTENSIONS
68 MPACK_TYPE_STRING_CASE(mpack_type_ext);
69 #endif
70 #undef MPACK_TYPE_STRING_CASE
71 }
72 mpack_assert(0, "unrecognized type %i", (int)type);
73 return "(unknown mpack_type_t)";
74 #else
75 MPACK_UNUSED(type);
76 return "";
77 #endif
78}
79
80int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) {
81
82 // positive numbers may be stored as int; convert to uint
83 if (left.type == mpack_type_int && left.v.i >= 0) {
84 left.type = mpack_type_uint;
85 left.v.u = (uint64_t)left.v.i;
86 }
87 if (right.type == mpack_type_int && right.v.i >= 0) {
88 right.type = mpack_type_uint;
89 right.v.u = (uint64_t)right.v.i;
90 }
91
92 if (left.type != right.type)
93 return ((int)left.type < (int)right.type) ? -1 : 1;
94
95 switch (left.type) {
96 case mpack_type_missing: // fallthrough
97 case mpack_type_nil:
98 return 0;
99
100 case mpack_type_bool:
101 return (int)left.v.b - (int)right.v.b;
102
103 case mpack_type_int:
104 if (left.v.i == right.v.i)
105 return 0;
106 return (left.v.i < right.v.i) ? -1 : 1;
107
108 case mpack_type_uint:
109 if (left.v.u == right.v.u)
110 return 0;
111 return (left.v.u < right.v.u) ? -1 : 1;
112
113 case mpack_type_array:
114 case mpack_type_map:
115 if (left.v.n == right.v.n)
116 return 0;
117 return (left.v.n < right.v.n) ? -1 : 1;
118
119 case mpack_type_str:
120 case mpack_type_bin:
121 if (left.v.l == right.v.l)
122 return 0;
123 return (left.v.l < right.v.l) ? -1 : 1;
124
125 #if MPACK_EXTENSIONS
126 case mpack_type_ext:
127 if (left.exttype == right.exttype) {
128 if (left.v.l == right.v.l)
129 return 0;
130 return (left.v.l < right.v.l) ? -1 : 1;
131 }
132 return (int)left.exttype - (int)right.exttype;
133 #endif
134
135 // floats should not normally be compared for equality. we compare
136 // with memcmp() to silence compiler warnings, but this will return
137 // equal if both are NaNs with the same representation (though we may
138 // want this, for instance if you are for some bizarre reason using
139 // floats as map keys.) i'm not sure what the right thing to
140 // do is here. check for NaN first? always return false if the type
141 // is float? use operator== and pragmas to silence compiler warning?
142 // please send me your suggestions.
143 // note also that we don't convert floats to doubles, so when this is
144 // used for ordering purposes, all floats are ordered before all
145 // doubles.
146 case mpack_type_float:
147 return mpack_memcmp(&left.v.f, &right.v.f, sizeof(left.v.f));
148 case mpack_type_double:
149 return mpack_memcmp(&left.v.d, &right.v.d, sizeof(left.v.d));
150 }
151
152 mpack_assert(0, "unrecognized type %i", (int)left.type);
153 return false;
154}
155
156#if MPACK_DEBUG && MPACK_STDIO
157static char mpack_hex_char(uint8_t hex_value) {
158 // Older compilers (e.g. GCC 4.4.7) promote the result of this ternary to
159 // int and warn under -Wconversion, so we have to cast it back to char.
160 return (char)((hex_value < 10) ? (char)('0' + hex_value) : (char)('a' + (hex_value - 10)));
161}
162
163static void mpack_tag_debug_complete_bin_ext(mpack_tag_t tag, size_t string_length, char* buffer, size_t buffer_size,
164 const char* prefix, size_t prefix_size)
165{
166 // If at any point in this function we run out of space in the buffer, we
167 // bail out. The outer tag print wrapper will make sure we have a
168 // null-terminator.
169
170 if (string_length == 0 || string_length >= buffer_size)
171 return;
172 buffer += string_length;
173 buffer_size -= string_length;
174
175 size_t total = mpack_tag_bytes(&tag);
176 if (total == 0) {
177 strncpy(buffer, ">", buffer_size);
178 return;
179 }
180
181 strncpy(buffer, ": ", buffer_size);
182 if (buffer_size < 2)
183 return;
184 buffer += 2;
185 buffer_size -= 2;
186
187 size_t hex_bytes = 0;
188 size_t i;
189 for (i = 0; i < MPACK_PRINT_BYTE_COUNT && i < prefix_size && buffer_size > 2; ++i) {
190 uint8_t byte = (uint8_t)prefix[i];
191 buffer[0] = mpack_hex_char((uint8_t)(byte >> 4));
192 buffer[1] = mpack_hex_char((uint8_t)(byte & 0xfu));
193 buffer += 2;
194 buffer_size -= 2;
195 ++hex_bytes;
196 }
197
198 if (buffer_size != 0)
199 mpack_snprintf(buffer, buffer_size, "%s>", (total > hex_bytes) ? "..." : "");
200}
201
202static void mpack_tag_debug_pseudo_json_bin(mpack_tag_t tag, char* buffer, size_t buffer_size,
203 const char* prefix, size_t prefix_size)
204{
205 mpack_assert(mpack_tag_type(&tag) == mpack_type_bin);
206 size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<binary data of length %" PRIu32 "", tag.v.l);
207 mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
208}
209
210#if MPACK_EXTENSIONS
211static void mpack_tag_debug_pseudo_json_ext(mpack_tag_t tag, char* buffer, size_t buffer_size,
212 const char* prefix, size_t prefix_size)
213{
214 mpack_assert(mpack_tag_type(&tag) == mpack_type_ext);
215 size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<ext data of type %i and length %" PRIu32 "",
216 mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
217 mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
218}
219#endif
220
221static void mpack_tag_debug_pseudo_json_impl(mpack_tag_t tag, char* buffer, size_t buffer_size,
222 const char* prefix, size_t prefix_size)
223{
224 switch (tag.type) {
225 case mpack_type_missing:
226 mpack_snprintf(buffer, buffer_size, "<missing!>");
227 return;
228 case mpack_type_nil:
229 mpack_snprintf(buffer, buffer_size, "null");
230 return;
231 case mpack_type_bool:
232 mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
233 return;
234 case mpack_type_int:
235 mpack_snprintf(buffer, buffer_size, "%" PRIi64, tag.v.i);
236 return;
237 case mpack_type_uint:
238 mpack_snprintf(buffer, buffer_size, "%" PRIu64, tag.v.u);
239 return;
240 case mpack_type_float:
241 #if MPACK_FLOAT
242 mpack_snprintf(buffer, buffer_size, "%f", tag.v.f);
243 #else
244 mpack_snprintf(buffer, buffer_size, "<float>");
245 #endif
246 return;
247 case mpack_type_double:
248 #if MPACK_DOUBLE
249 mpack_snprintf(buffer, buffer_size, "%f", tag.v.d);
250 #else
251 mpack_snprintf(buffer, buffer_size, "<double>");
252 #endif
253 return;
254
255 case mpack_type_str:
256 mpack_snprintf(buffer, buffer_size, "<string of %" PRIu32 " bytes>", tag.v.l);
257 return;
258 case mpack_type_bin:
259 mpack_tag_debug_pseudo_json_bin(tag, buffer, buffer_size, prefix, prefix_size);
260 return;
261 #if MPACK_EXTENSIONS
262 case mpack_type_ext:
263 mpack_tag_debug_pseudo_json_ext(tag, buffer, buffer_size, prefix, prefix_size);
264 return;
265 #endif
266
267 case mpack_type_array:
268 mpack_snprintf(buffer, buffer_size, "<array of %" PRIu32 " elements>", tag.v.n);
269 return;
270 case mpack_type_map:
271 mpack_snprintf(buffer, buffer_size, "<map of %" PRIu32 " key-value pairs>", tag.v.n);
272 return;
273 }
274
275 mpack_snprintf(buffer, buffer_size, "<unknown!>");
276}
277
278void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_size,
279 const char* prefix, size_t prefix_size)
280{
281 mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
282 buffer[0] = 0;
283
284 mpack_tag_debug_pseudo_json_impl(tag, buffer, buffer_size, prefix, prefix_size);
285
286 // We always null-terminate the buffer manually just in case the snprintf()
287 // function doesn't null-terminate when the string doesn't fit.
288 buffer[buffer_size - 1] = 0;
289}
290
291static void mpack_tag_debug_describe_impl(mpack_tag_t tag, char* buffer, size_t buffer_size) {
292 switch (tag.type) {
293 case mpack_type_missing:
294 mpack_snprintf(buffer, buffer_size, "missing");
295 return;
296 case mpack_type_nil:
297 mpack_snprintf(buffer, buffer_size, "nil");
298 return;
299 case mpack_type_bool:
300 mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
301 return;
302 case mpack_type_int:
303 mpack_snprintf(buffer, buffer_size, "int %" PRIi64, tag.v.i);
304 return;
305 case mpack_type_uint:
306 mpack_snprintf(buffer, buffer_size, "uint %" PRIu64, tag.v.u);
307 return;
308 case mpack_type_float:
309 #if MPACK_FLOAT
310 mpack_snprintf(buffer, buffer_size, "float %f", tag.v.f);
311 #else
312 mpack_snprintf(buffer, buffer_size, "float");
313 #endif
314 return;
315 case mpack_type_double:
316 #if MPACK_DOUBLE
317 mpack_snprintf(buffer, buffer_size, "double %f", tag.v.d);
318 #else
319 mpack_snprintf(buffer, buffer_size, "double");
320 #endif
321 return;
322 case mpack_type_str:
323 mpack_snprintf(buffer, buffer_size, "str of %" PRIu32 " bytes", tag.v.l);
324 return;
325 case mpack_type_bin:
326 mpack_snprintf(buffer, buffer_size, "bin of %" PRIu32 " bytes", tag.v.l);
327 return;
328 #if MPACK_EXTENSIONS
329 case mpack_type_ext:
330 mpack_snprintf(buffer, buffer_size, "ext of type %i, %" PRIu32 " bytes",
331 mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
332 return;
333 #endif
334 case mpack_type_array:
335 mpack_snprintf(buffer, buffer_size, "array of %" PRIu32 " elements", tag.v.n);
336 return;
337 case mpack_type_map:
338 mpack_snprintf(buffer, buffer_size, "map of %" PRIu32 " key-value pairs", tag.v.n);
339 return;
340 }
341
342 mpack_snprintf(buffer, buffer_size, "unknown!");
343}
344
345void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size) {
346 mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
347 buffer[0] = 0;
348
349 mpack_tag_debug_describe_impl(tag, buffer, buffer_size);
350
351 // We always null-terminate the buffer manually just in case the snprintf()
352 // function doesn't null-terminate when the string doesn't fit.
353 buffer[buffer_size - 1] = 0;
354}
355#endif
356
357
358
359#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
360
361#ifndef MPACK_TRACKING_INITIAL_CAPACITY
362// seems like a reasonable number. we grow by doubling, and it only
363// needs to be as long as the maximum depth of the message.
364#define MPACK_TRACKING_INITIAL_CAPACITY 8
365#endif
366
367mpack_error_t mpack_track_init(mpack_track_t* track) {
368 track->count = 0;
369 track->capacity = MPACK_TRACKING_INITIAL_CAPACITY;
370 track->elements = (mpack_track_element_t*)MPACK_MALLOC(sizeof(mpack_track_element_t) * track->capacity);
371 if (track->elements == NULL)
372 return mpack_error_memory;
373 return mpack_ok;
374}
375
376mpack_error_t mpack_track_grow(mpack_track_t* track) {
377 mpack_assert(track->elements, "null track elements!");
378 mpack_assert(track->count == track->capacity, "incorrect growing?");
379
380 size_t new_capacity = track->capacity * 2;
381
382 mpack_track_element_t* new_elements = (mpack_track_element_t*)mpack_realloc(track->elements,
383 sizeof(mpack_track_element_t) * track->count, sizeof(mpack_track_element_t) * new_capacity);
384 if (new_elements == NULL)
385 return mpack_error_memory;
386
387 track->elements = new_elements;
388 track->capacity = new_capacity;
389 return mpack_ok;
390}
391
392mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint32_t count) {
393 mpack_assert(track->elements, "null track elements!");
394 mpack_log("track pushing %s count %i\n", mpack_type_to_string(type), (int)count);
395
396 // grow if needed
397 if (track->count == track->capacity) {
398 mpack_error_t error = mpack_track_grow(track);
399 if (error != mpack_ok)
400 return error;
401 }
402
403 // insert new track
404 track->elements[track->count].type = type;
405 track->elements[track->count].left = count;
406 track->elements[track->count].builder = false;
407 track->elements[track->count].key_needs_value = false;
408 ++track->count;
409 return mpack_ok;
410}
411
412// TODO dedupe this
413mpack_error_t mpack_track_push_builder(mpack_track_t* track, mpack_type_t type) {
414 mpack_assert(track->elements, "null track elements!");
415 mpack_log("track pushing %s builder\n", mpack_type_to_string(type));
416
417 // grow if needed
418 if (track->count == track->capacity) {
419 mpack_error_t error = mpack_track_grow(track);
420 if (error != mpack_ok)
421 return error;
422 }
423
424 // insert new track
425 track->elements[track->count].type = type;
426 track->elements[track->count].left = 0;
427 track->elements[track->count].builder = true;
428 track->elements[track->count].key_needs_value = false;
429 ++track->count;
430 return mpack_ok;
431}
432
433static mpack_error_t mpack_track_pop_impl(mpack_track_t* track, mpack_type_t type, bool builder) {
434 mpack_assert(track->elements, "null track elements!");
435 mpack_log("track popping %s\n", mpack_type_to_string(type));
436
437 if (track->count == 0) {
438 mpack_break("attempting to close a %s but nothing was opened!", mpack_type_to_string(type));
439 return mpack_error_bug;
440 }
441
442 mpack_track_element_t* element = &track->elements[track->count - 1];
443
444 if (element->type != type) {
445 mpack_break("attempting to close a %s but the open element is a %s!",
446 mpack_type_to_string(type), mpack_type_to_string(element->type));
447 return mpack_error_bug;
448 }
449
450 if (element->key_needs_value) {
451 mpack_assert(type == mpack_type_map, "key_needs_value can only be true for maps!");
452 mpack_break("attempting to close a %s but an odd number of elements were written",
453 mpack_type_to_string(type));
454 return mpack_error_bug;
455 }
456
457 if (element->left != 0) {
458 mpack_break("attempting to close a %s but there are %i %s left",
459 mpack_type_to_string(type), element->left,
460 (type == mpack_type_map || type == mpack_type_array) ? "elements" : "bytes");
461 return mpack_error_bug;
462 }
463
464 if (element->builder != builder) {
465 mpack_break("attempting to pop a %sbuilder but the open element is %sa builder",
466 builder ? "" : "non-",
467 element->builder ? "" : "not ");
468 return mpack_error_bug;
469 }
470
471 --track->count;
472 return mpack_ok;
473}
474
475mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) {
476 return mpack_track_pop_impl(track, type, false);
477}
478
479mpack_error_t mpack_track_pop_builder(mpack_track_t* track, mpack_type_t type) {
480 return mpack_track_pop_impl(track, type, true);
481}
482
483mpack_error_t mpack_track_peek_element(mpack_track_t* track, bool read) {
484 MPACK_UNUSED(read);
485 mpack_assert(track->elements, "null track elements!");
486
487 // if there are no open elements, that's fine, we can read/write elements at will
488 if (track->count == 0)
489 return mpack_ok;
490
491 mpack_track_element_t* element = &track->elements[track->count - 1];
492
493 if (element->type != mpack_type_map && element->type != mpack_type_array) {
494 mpack_break("elements cannot be %s within an %s", read ? "read" : "written",
495 mpack_type_to_string(element->type));
496 return mpack_error_bug;
497 }
498
499 if (!element->builder && element->left == 0 && !element->key_needs_value) {
500 mpack_break("too many elements %s for %s", read ? "read" : "written",
501 mpack_type_to_string(element->type));
502 return mpack_error_bug;
503 }
504
505 return mpack_ok;
506}
507
508mpack_error_t mpack_track_element(mpack_track_t* track, bool read) {
509 mpack_error_t error = mpack_track_peek_element(track, read);
510 if (track->count == 0 || error != mpack_ok)
511 return error;
512
513 mpack_track_element_t* element = &track->elements[track->count - 1];
514
515 if (element->type == mpack_type_map) {
516 if (!element->key_needs_value) {
517 element->key_needs_value = true;
518 return mpack_ok; // don't decrement
519 }
520 element->key_needs_value = false;
521 }
522
523 if (!element->builder)
524 --element->left;
525 return mpack_ok;
526}
527
528mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, size_t count) {
529 MPACK_UNUSED(read);
530 mpack_assert(track->elements, "null track elements!");
531
532 if (count > MPACK_UINT32_MAX) {
533 mpack_break("%s more bytes than could possibly fit in a str/bin/ext!",
534 read ? "reading" : "writing");
535 return mpack_error_bug;
536 }
537
538 if (track->count == 0) {
539 mpack_break("bytes cannot be %s with no open bin, str or ext", read ? "read" : "written");
540 return mpack_error_bug;
541 }
542
543 mpack_track_element_t* element = &track->elements[track->count - 1];
544
545 if (element->type == mpack_type_map || element->type == mpack_type_array) {
546 mpack_break("bytes cannot be %s within an %s", read ? "read" : "written",
547 mpack_type_to_string(element->type));
548 return mpack_error_bug;
549 }
550
551 if (element->left < count) {
552 mpack_break("too many bytes %s for %s", read ? "read" : "written",
553 mpack_type_to_string(element->type));
554 return mpack_error_bug;
555 }
556
557 element->left -= (uint32_t)count;
558 return mpack_ok;
559}
560
561mpack_error_t mpack_track_str_bytes_all(mpack_track_t* track, bool read, size_t count) {
562 mpack_error_t error = mpack_track_bytes(track, read, count);
563 if (error != mpack_ok)
564 return error;
565
566 mpack_track_element_t* element = &track->elements[track->count - 1];
567
568 if (element->type != mpack_type_str) {
569 mpack_break("the open type must be a string, not a %s", mpack_type_to_string(element->type));
570 return mpack_error_bug;
571 }
572
573 if (element->left != 0) {
574 mpack_break("not all bytes were read; the wrong byte count was requested for a string read.");
575 return mpack_error_bug;
576 }
577
578 return mpack_ok;
579}
580
581mpack_error_t mpack_track_check_empty(mpack_track_t* track) {
582 if (track->count != 0) {
583 mpack_break("unclosed %s", mpack_type_to_string(track->elements[0].type));
584 return mpack_error_bug;
585 }
586 return mpack_ok;
587}
588
589mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) {
590 mpack_error_t error = cancel ? mpack_ok : mpack_track_check_empty(track);
591 if (track->elements) {
592 MPACK_FREE(track->elements);
593 track->elements = NULL;
594 }
595 return error;
596}
597#endif
598
599
600
601static bool mpack_utf8_check_impl(const uint8_t* str, size_t count, bool allow_null) {
602 while (count > 0) {
603 uint8_t lead = str[0];
604
605 // NUL
606 if (!allow_null && lead == '\0') // we don't allow NUL bytes in MPack C-strings
607 return false;
608
609 // ASCII
610 if (lead <= 0x7F) {
611 ++str;
612 --count;
613
614 // 2-byte sequence
615 } else if ((lead & 0xE0) == 0xC0) {
616 if (count < 2) // truncated sequence
617 return false;
618
619 uint8_t cont = str[1];
620 if ((cont & 0xC0) != 0x80) // not a continuation byte
621 return false;
622
623 str += 2;
624 count -= 2;
625
626 uint32_t z = ((uint32_t)(lead & ~0xE0) << 6) |
627 (uint32_t)(cont & ~0xC0);
628
629 if (z < 0x80) // overlong sequence
630 return false;
631
632 // 3-byte sequence
633 } else if ((lead & 0xF0) == 0xE0) {
634 if (count < 3) // truncated sequence
635 return false;
636
637 uint8_t cont1 = str[1];
638 if ((cont1 & 0xC0) != 0x80) // not a continuation byte
639 return false;
640 uint8_t cont2 = str[2];
641 if ((cont2 & 0xC0) != 0x80) // not a continuation byte
642 return false;
643
644 str += 3;
645 count -= 3;
646
647 uint32_t z = ((uint32_t)(lead & ~0xF0) << 12) |
648 ((uint32_t)(cont1 & ~0xC0) << 6) |
649 (uint32_t)(cont2 & ~0xC0);
650
651 if (z < 0x800) // overlong sequence
652 return false;
653 if (z >= 0xD800 && z <= 0xDFFF) // surrogate
654 return false;
655
656 // 4-byte sequence
657 } else if ((lead & 0xF8) == 0xF0) {
658 if (count < 4) // truncated sequence
659 return false;
660
661 uint8_t cont1 = str[1];
662 if ((cont1 & 0xC0) != 0x80) // not a continuation byte
663 return false;
664 uint8_t cont2 = str[2];
665 if ((cont2 & 0xC0) != 0x80) // not a continuation byte
666 return false;
667 uint8_t cont3 = str[3];
668 if ((cont3 & 0xC0) != 0x80) // not a continuation byte
669 return false;
670
671 str += 4;
672 count -= 4;
673
674 uint32_t z = ((uint32_t)(lead & ~0xF8) << 18) |
675 ((uint32_t)(cont1 & ~0xC0) << 12) |
676 ((uint32_t)(cont2 & ~0xC0) << 6) |
677 (uint32_t)(cont3 & ~0xC0);
678
679 if (z < 0x10000) // overlong sequence
680 return false;
681 if (z > 0x10FFFF) // codepoint limit
682 return false;
683
684 } else {
685 return false; // continuation byte without a lead, or lead for a 5-byte sequence or longer
686 }
687 }
688 return true;
689}
690
691bool mpack_utf8_check(const char* str, size_t bytes) {
692 return mpack_utf8_check_impl((const uint8_t*)str, bytes, true);
693}
694
695bool mpack_utf8_check_no_null(const char* str, size_t bytes) {
696 return mpack_utf8_check_impl((const uint8_t*)str, bytes, false);
697}
698
699bool mpack_str_check_no_null(const char* str, size_t bytes) {
700 size_t i;
701 for (i = 0; i < bytes; ++i)
702 if (str[i] == '\0')
703 return false;
704 return true;
705}
706
707#if MPACK_DEBUG && MPACK_STDIO
708void mpack_print_append(mpack_print_t* print, const char* data, size_t count) {
709
710 // copy whatever fits into the buffer
711 size_t copy = print->size - print->count;
712 if (copy > count)
713 copy = count;
714 mpack_memcpy(print->buffer + print->count, data, copy);
715 print->count += copy;
716 data += copy;
717 count -= copy;
718
719 // if we don't need to flush or can't flush there's nothing else to do
720 if (count == 0 || print->callback == NULL)
721 return;
722
723 // flush the buffer
724 print->callback(print->context, print->buffer, print->count);
725
726 if (count > print->size / 2) {
727 // flush the rest of the data
728 print->count = 0;
729 print->callback(print->context, data, count);
730 } else {
731 // copy the rest of the data into the buffer
732 mpack_memcpy(print->buffer, data, count);
733 print->count = count;
734 }
735
736}
737
738void mpack_print_flush(mpack_print_t* print) {
739 if (print->count > 0 && print->callback != NULL) {
740 print->callback(print->context, print->buffer, print->count);
741 print->count = 0;
742 }
743}
744
745void mpack_print_file_callback(void* context, const char* data, size_t count) {
746 FILE* file = (FILE*)context;
747 fwrite(data, 1, count, file);
748}
749#endif
750
751MPACK_SILENCE_WARNINGS_END
752