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 | |
26 | MPACK_SILENCE_WARNINGS_BEGIN |
27 | |
28 | const 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 | |
52 | const 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 | |
80 | int 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 |
157 | static 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 | |
163 | static 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 | |
202 | static 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 |
211 | static 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 | |
221 | static 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 | |
278 | void 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 | |
291 | static 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 | |
345 | void 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 | |
367 | mpack_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 | |
376 | mpack_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 | |
392 | mpack_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 |
413 | mpack_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 | |
433 | static 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 | |
475 | mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) { |
476 | return mpack_track_pop_impl(track, type, false); |
477 | } |
478 | |
479 | mpack_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 | |
483 | mpack_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 | |
508 | mpack_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 | |
528 | mpack_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 | |
561 | mpack_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 | |
581 | mpack_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 | |
589 | mpack_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 | |
601 | static 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 | |
691 | bool mpack_utf8_check(const char* str, size_t bytes) { |
692 | return mpack_utf8_check_impl((const uint8_t*)str, bytes, true); |
693 | } |
694 | |
695 | bool 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 | |
699 | bool 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 |
708 | void 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 | |
738 | void 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 | |
745 | void 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 | |
751 | MPACK_SILENCE_WARNINGS_END |
752 | |