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-writer.h"
25
26MPACK_SILENCE_WARNINGS_BEGIN
27
28#if MPACK_WRITER
29
30#if MPACK_BUILDER
31static void mpack_builder_flush(mpack_writer_t* writer);
32#endif
33
34#if MPACK_WRITE_TRACKING
35static void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_error_t error) {
36 if (error != mpack_ok)
37 mpack_writer_flag_error(writer, error);
38}
39
40void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count) {
41 if (writer->error == mpack_ok)
42 mpack_writer_flag_if_error(writer, mpack_track_push(&writer->track, type, count));
43}
44
45void mpack_writer_track_push_builder(mpack_writer_t* writer, mpack_type_t type) {
46 if (writer->error == mpack_ok)
47 mpack_writer_flag_if_error(writer, mpack_track_push_builder(&writer->track, type));
48}
49
50void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type) {
51 if (writer->error == mpack_ok)
52 mpack_writer_flag_if_error(writer, mpack_track_pop(&writer->track, type));
53}
54
55void mpack_writer_track_pop_builder(mpack_writer_t* writer, mpack_type_t type) {
56 if (writer->error == mpack_ok)
57 mpack_writer_flag_if_error(writer, mpack_track_pop_builder(&writer->track, type));
58}
59
60void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count) {
61 if (writer->error == mpack_ok)
62 mpack_writer_flag_if_error(writer, mpack_track_bytes(&writer->track, false, count));
63}
64#endif
65
66// This should probably be renamed. It's not solely used for tracking.
67static inline void mpack_writer_track_element(mpack_writer_t* writer) {
68 (void)writer;
69
70 #if MPACK_WRITE_TRACKING
71 if (writer->error == mpack_ok)
72 mpack_writer_flag_if_error(writer, mpack_track_element(&writer->track, false));
73 #endif
74
75 #if MPACK_BUILDER
76 if (writer->builder.current_build != NULL) {
77 mpack_build_t* build = writer->builder.current_build;
78 // We only track this write if it's not nested within another non-build
79 // map or array.
80 if (build->nested_compound_elements == 0) {
81 if (build->type != mpack_type_map) {
82 ++build->count;
83 mpack_log("adding element to build %p, now %" PRIu32 " elements\n", (void*)build, build->count);
84 } else if (build->key_needs_value) {
85 build->key_needs_value = false;
86 ++build->count;
87 } else {
88 build->key_needs_value = true;
89 }
90 }
91 }
92 #endif
93}
94
95static void mpack_writer_clear(mpack_writer_t* writer) {
96 #if MPACK_COMPATIBILITY
97 writer->version = mpack_version_current;
98 #endif
99 writer->flush = NULL;
100 writer->error_fn = NULL;
101 writer->teardown = NULL;
102 writer->context = NULL;
103
104 writer->buffer = NULL;
105 writer->position = NULL;
106 writer->end = NULL;
107 writer->error = mpack_ok;
108
109 #if MPACK_WRITE_TRACKING
110 mpack_memset(&writer->track, 0, sizeof(writer->track));
111 #endif
112
113 #if MPACK_BUILDER
114 writer->builder.current_build = NULL;
115 writer->builder.latest_build = NULL;
116 writer->builder.current_page = NULL;
117 writer->builder.pages = NULL;
118 writer->builder.stash_buffer = NULL;
119 writer->builder.stash_position = NULL;
120 writer->builder.stash_end = NULL;
121 #endif
122}
123
124void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size) {
125 mpack_assert(buffer != NULL, "cannot initialize writer with empty buffer");
126 mpack_writer_clear(writer);
127 writer->buffer = buffer;
128 writer->position = buffer;
129 writer->end = writer->buffer + size;
130
131 #if MPACK_WRITE_TRACKING
132 mpack_writer_flag_if_error(writer, mpack_track_init(&writer->track));
133 #endif
134
135 mpack_log("===========================\n");
136 mpack_log("initializing writer with buffer size %i\n", (int)size);
137}
138
139void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error) {
140 mpack_writer_clear(writer);
141 writer->error = error;
142
143 mpack_log("===========================\n");
144 mpack_log("initializing writer in error state %i\n", (int)error);
145}
146
147void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush) {
148 MPACK_STATIC_ASSERT(MPACK_WRITER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
149 "minimum buffer size must fit any tag!");
150 MPACK_STATIC_ASSERT(31 + MPACK_TAG_SIZE_FIXSTR >= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
151 "minimum buffer size must fit the largest possible fixstr!");
152
153 if (mpack_writer_buffer_size(writer) < MPACK_WRITER_MINIMUM_BUFFER_SIZE) {
154 mpack_break("buffer size is %i, but minimum buffer size for flush is %i",
155 (int)mpack_writer_buffer_size(writer), MPACK_WRITER_MINIMUM_BUFFER_SIZE);
156 mpack_writer_flag_error(writer, mpack_error_bug);
157 return;
158 }
159
160 writer->flush = flush;
161}
162
163#ifdef MPACK_MALLOC
164typedef struct mpack_growable_writer_t {
165 char** target_data;
166 size_t* target_size;
167} mpack_growable_writer_t;
168
169static char* mpack_writer_get_reserved(mpack_writer_t* writer) {
170 // This is in a separate function in order to avoid false strict aliasing
171 // warnings. We aren't actually violating strict aliasing (the reserved
172 // space is only ever dereferenced as an mpack_growable_writer_t.)
173 return (char*)writer->reserved;
174}
175
176static void mpack_growable_writer_flush(mpack_writer_t* writer, const char* data, size_t count) {
177
178 // This is an intrusive flush function which modifies the writer's buffer
179 // in response to a flush instead of emptying it in order to add more
180 // capacity for data. This removes the need to copy data from a fixed buffer
181 // into a growable one, improving performance.
182 //
183 // There are three ways flush can be called:
184 // - flushing the buffer during writing (used is zero, count is all data, data is buffer)
185 // - flushing extra data during writing (used is all flushed data, count is extra data, data is not buffer)
186 // - flushing during teardown (used and count are both all flushed data, data is buffer)
187 //
188 // In the first two cases, we grow the buffer by at least double, enough
189 // to ensure that new data will fit. We ignore the teardown flush.
190
191 if (data == writer->buffer) {
192
193 // teardown, do nothing
194 if (mpack_writer_buffer_used(writer) == count)
195 return;
196
197 // otherwise leave the data in the buffer and just grow
198 writer->position = writer->buffer + count;
199 count = 0;
200 }
201
202 size_t used = mpack_writer_buffer_used(writer);
203 size_t size = mpack_writer_buffer_size(writer);
204
205 mpack_log("flush size %i used %i data %p buffer %p\n",
206 (int)count, (int)used, data, writer->buffer);
207
208 mpack_assert(data == writer->buffer || used + count > size,
209 "extra flush for %i but there is %i space left in the buffer! (%i/%i)",
210 (int)count, (int)mpack_writer_buffer_left(writer), (int)used, (int)size);
211
212 // grow to fit the data
213 // TODO: this really needs to correctly test for overflow
214 size_t new_size = size * 2;
215 while (new_size < used + count)
216 new_size *= 2;
217
218 mpack_log("flush growing buffer size from %i to %i\n", (int)size, (int)new_size);
219
220 // grow the buffer
221 char* new_buffer = (char*)mpack_realloc(writer->buffer, used, new_size);
222 if (new_buffer == NULL) {
223 mpack_writer_flag_error(writer, mpack_error_memory);
224 return;
225 }
226 writer->position = new_buffer + used;
227 writer->buffer = new_buffer;
228 writer->end = writer->buffer + new_size;
229
230 // append the extra data
231 if (count > 0) {
232 mpack_memcpy(writer->position, data, count);
233 writer->position += count;
234 }
235
236 mpack_log("new buffer %p, used %i\n", new_buffer, (int)mpack_writer_buffer_used(writer));
237}
238
239static void mpack_growable_writer_teardown(mpack_writer_t* writer) {
240 mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
241
242 if (mpack_writer_error(writer) == mpack_ok) {
243
244 // shrink the buffer to an appropriate size if the data is
245 // much smaller than the buffer
246 if (mpack_writer_buffer_used(writer) < mpack_writer_buffer_size(writer) / 2) {
247 size_t used = mpack_writer_buffer_used(writer);
248
249 // We always return a non-null pointer that must be freed, even if
250 // nothing was written. malloc() and realloc() do not necessarily
251 // do this so we enforce it ourselves.
252 size_t size = (used != 0) ? used : 1;
253
254 char* buffer = (char*)mpack_realloc(writer->buffer, used, size);
255 if (!buffer) {
256 MPACK_FREE(writer->buffer);
257 mpack_writer_flag_error(writer, mpack_error_memory);
258 return;
259 }
260 writer->buffer = buffer;
261 writer->end = (writer->position = writer->buffer + used);
262 }
263
264 *growable_writer->target_data = writer->buffer;
265 *growable_writer->target_size = mpack_writer_buffer_used(writer);
266 writer->buffer = NULL;
267
268 } else if (writer->buffer) {
269 MPACK_FREE(writer->buffer);
270 writer->buffer = NULL;
271 }
272
273 writer->context = NULL;
274}
275
276void mpack_writer_init_growable(mpack_writer_t* writer, char** target_data, size_t* target_size) {
277 mpack_assert(target_data != NULL, "cannot initialize writer without a destination for the data");
278 mpack_assert(target_size != NULL, "cannot initialize writer without a destination for the size");
279
280 *target_data = NULL;
281 *target_size = 0;
282
283 MPACK_STATIC_ASSERT(sizeof(mpack_growable_writer_t) <= sizeof(writer->reserved),
284 "not enough reserved space for growable writer!");
285 mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
286
287 growable_writer->target_data = target_data;
288 growable_writer->target_size = target_size;
289
290 size_t capacity = MPACK_BUFFER_SIZE;
291 char* buffer = (char*)MPACK_MALLOC(capacity);
292 if (buffer == NULL) {
293 mpack_writer_init_error(writer, mpack_error_memory);
294 return;
295 }
296
297 mpack_writer_init(writer, buffer, capacity);
298 mpack_writer_set_flush(writer, mpack_growable_writer_flush);
299 mpack_writer_set_teardown(writer, mpack_growable_writer_teardown);
300}
301#endif
302
303#if MPACK_STDIO
304static void mpack_file_writer_flush(mpack_writer_t* writer, const char* buffer, size_t count) {
305 FILE* file = (FILE*)writer->context;
306 size_t written = fwrite((const void*)buffer, 1, count, file);
307 if (written != count)
308 mpack_writer_flag_error(writer, mpack_error_io);
309}
310
311static void mpack_file_writer_teardown(mpack_writer_t* writer) {
312 MPACK_FREE(writer->buffer);
313 writer->buffer = NULL;
314 writer->context = NULL;
315}
316
317static void mpack_file_writer_teardown_close(mpack_writer_t* writer) {
318 FILE* file = (FILE*)writer->context;
319
320 if (file) {
321 int ret = fclose(file);
322 if (ret != 0)
323 mpack_writer_flag_error(writer, mpack_error_io);
324 }
325
326 mpack_file_writer_teardown(writer);
327}
328
329void mpack_writer_init_stdfile(mpack_writer_t* writer, FILE* file, bool close_when_done) {
330 mpack_assert(file != NULL, "file is NULL");
331
332 size_t capacity = MPACK_BUFFER_SIZE;
333 char* buffer = (char*)MPACK_MALLOC(capacity);
334 if (buffer == NULL) {
335 mpack_writer_init_error(writer, mpack_error_memory);
336 if (close_when_done) {
337 fclose(file);
338 }
339 return;
340 }
341
342 mpack_writer_init(writer, buffer, capacity);
343 mpack_writer_set_context(writer, file);
344 mpack_writer_set_flush(writer, mpack_file_writer_flush);
345 mpack_writer_set_teardown(writer, close_when_done ?
346 mpack_file_writer_teardown_close :
347 mpack_file_writer_teardown);
348}
349
350void mpack_writer_init_filename(mpack_writer_t* writer, const char* filename) {
351 mpack_assert(filename != NULL, "filename is NULL");
352
353 FILE* file = fopen(filename, "wb");
354 if (file == NULL) {
355 mpack_writer_init_error(writer, mpack_error_io);
356 return;
357 }
358
359 mpack_writer_init_stdfile(writer, file, true);
360}
361#endif
362
363void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error) {
364 mpack_log("writer %p setting error %i: %s\n", (void*)writer, (int)error, mpack_error_to_string(error));
365
366 if (writer->error == mpack_ok) {
367 writer->error = error;
368 if (writer->error_fn)
369 writer->error_fn(writer, writer->error);
370 }
371}
372
373MPACK_STATIC_INLINE void mpack_writer_flush_unchecked(mpack_writer_t* writer) {
374 // This is a bit ugly; we reset used before calling flush so that
375 // a flush function can distinguish between flushing the buffer
376 // versus flushing external data. see mpack_growable_writer_flush()
377 size_t used = mpack_writer_buffer_used(writer);
378 writer->position = writer->buffer;
379 writer->flush(writer, writer->buffer, used);
380}
381
382void mpack_writer_flush_message(mpack_writer_t* writer) {
383 if (writer->error != mpack_ok)
384 return;
385
386 #if MPACK_WRITE_TRACKING
387 // You cannot flush while there are elements open.
388 mpack_writer_flag_if_error(writer, mpack_track_check_empty(&writer->track));
389 if (writer->error != mpack_ok)
390 return;
391 #endif
392
393 #if MPACK_BUILDER
394 if (writer->builder.current_build != NULL) {
395 mpack_break("cannot call mpack_writer_flush_message() while there are elements open!");
396 mpack_writer_flag_error(writer, mpack_error_bug);
397 return;
398 }
399 #endif
400
401 if (writer->flush == NULL) {
402 mpack_break("cannot call mpack_writer_flush_message() without a flush function!");
403 mpack_writer_flag_error(writer, mpack_error_bug);
404 return;
405 }
406
407 if (mpack_writer_buffer_used(writer) > 0)
408 mpack_writer_flush_unchecked(writer);
409}
410
411// Ensures there are at least count bytes free in the buffer. This
412// will flag an error if the flush function fails to make enough
413// room in the buffer.
414MPACK_NOINLINE static bool mpack_writer_ensure(mpack_writer_t* writer, size_t count) {
415 mpack_assert(count != 0, "cannot ensure zero bytes!");
416 mpack_assert(count <= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
417 "cannot ensure %i bytes, this is more than the minimum buffer size %i!",
418 (int)count, (int)MPACK_WRITER_MINIMUM_BUFFER_SIZE);
419 mpack_assert(count > mpack_writer_buffer_left(writer),
420 "request to ensure %i bytes but there are already %i left in the buffer!",
421 (int)count, (int)mpack_writer_buffer_left(writer));
422
423 mpack_log("ensuring %i bytes, %i left\n", (int)count, (int)mpack_writer_buffer_left(writer));
424
425 if (mpack_writer_error(writer) != mpack_ok)
426 return false;
427
428 #if MPACK_BUILDER
429 // if we have a build in progress, we just ask the builder for a page.
430 // either it will have space for a tag, or it will flag a memory error.
431 if (writer->builder.current_build != NULL) {
432 mpack_builder_flush(writer);
433 return mpack_writer_error(writer) == mpack_ok;
434 }
435 #endif
436
437 if (writer->flush == NULL) {
438 mpack_writer_flag_error(writer, mpack_error_too_big);
439 return false;
440 }
441
442 mpack_writer_flush_unchecked(writer);
443 if (mpack_writer_error(writer) != mpack_ok)
444 return false;
445
446 if (mpack_writer_buffer_left(writer) >= count)
447 return true;
448
449 mpack_writer_flag_error(writer, mpack_error_io);
450 return false;
451}
452
453// Writes encoded bytes to the buffer when we already know the data
454// does not fit in the buffer (i.e. it straddles the edge of the
455// buffer.) If there is a flush function, it is guaranteed to be
456// called; otherwise mpack_error_too_big is raised.
457MPACK_NOINLINE static void mpack_write_native_straddle(mpack_writer_t* writer, const char* p, size_t count) {
458 mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
459
460 if (mpack_writer_error(writer) != mpack_ok)
461 return;
462 mpack_log("big write for %i bytes from %p, %i space left in buffer\n",
463 (int)count, p, (int)mpack_writer_buffer_left(writer));
464 mpack_assert(count > mpack_writer_buffer_left(writer),
465 "big write requested for %i bytes, but there is %i available "
466 "space in buffer. should have called mpack_write_native() instead",
467 (int)count, (int)(mpack_writer_buffer_left(writer)));
468
469 #if MPACK_BUILDER
470 // if we have a build in progress, we can't flush. we need to copy all
471 // bytes into as many build buffer pages as it takes.
472 if (writer->builder.current_build != NULL) {
473 while (true) {
474 size_t step = (size_t)(writer->end - writer->position);
475 if (step > count)
476 step = count;
477 mpack_memcpy(writer->position, p, step);
478 writer->position += step;
479 p += step;
480 count -= step;
481
482 if (count == 0)
483 return;
484
485 mpack_builder_flush(writer);
486 if (mpack_writer_error(writer) != mpack_ok)
487 return;
488 mpack_assert(writer->position != writer->end);
489 }
490 }
491 #endif
492
493 // we'll need a flush function
494 if (!writer->flush) {
495 mpack_writer_flag_error(writer, mpack_error_too_big);
496 return;
497 }
498
499 // flush the buffer
500 mpack_writer_flush_unchecked(writer);
501 if (mpack_writer_error(writer) != mpack_ok)
502 return;
503
504 // note that an intrusive flush function (such as mpack_growable_writer_flush())
505 // may have changed size and/or reset used to a non-zero value. we treat both as
506 // though they may have changed, and there may still be data in the buffer.
507
508 // flush the extra data directly if it doesn't fit in the buffer
509 if (count > mpack_writer_buffer_left(writer)) {
510 writer->flush(writer, p, count);
511 if (mpack_writer_error(writer) != mpack_ok)
512 return;
513 } else {
514 mpack_memcpy(writer->position, p, count);
515 writer->position += count;
516 }
517}
518
519// Writes encoded bytes to the buffer, flushing if necessary.
520MPACK_STATIC_INLINE void mpack_write_native(mpack_writer_t* writer, const char* p, size_t count) {
521 mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
522
523 if (mpack_writer_buffer_left(writer) < count) {
524 mpack_write_native_straddle(writer, p, count);
525 } else {
526 mpack_memcpy(writer->position, p, count);
527 writer->position += count;
528 }
529}
530
531mpack_error_t mpack_writer_destroy(mpack_writer_t* writer) {
532
533 // clean up tracking, asserting if we're not already in an error state
534 #if MPACK_WRITE_TRACKING
535 mpack_track_destroy(&writer->track, writer->error != mpack_ok);
536 #endif
537
538 #if MPACK_BUILDER
539 mpack_builder_t* builder = &writer->builder;
540 if (builder->current_build != NULL) {
541 // A builder is open!
542
543 // Flag an error, if there's not already an error. You can only skip
544 // closing any open compound types if a write error occurred. If there
545 // wasn't already an error, it's a bug, which will assert in debug.
546 if (mpack_writer_error(writer) == mpack_ok) {
547 mpack_break("writer cannot be destroyed with an incomplete builder unless "
548 "an error was flagged!");
549 mpack_writer_flag_error(writer, mpack_error_bug);
550 }
551
552 // Free any remaining builder pages
553 mpack_builder_page_t* page = builder->pages;
554 #if MPACK_BUILDER_INTERNAL_STORAGE
555 mpack_assert(page == (mpack_builder_page_t*)builder->internal);
556 page = page->next;
557 #endif
558 while (page != NULL) {
559 mpack_builder_page_t* next = page->next;
560 MPACK_FREE(page);
561 page = next;
562 }
563
564 // Restore the stashed pointers. The teardown function may need to free
565 // them (e.g. mpack_growable_writer_teardown().)
566 writer->buffer = builder->stash_buffer;
567 writer->position = builder->stash_position;
568 writer->end = builder->stash_end;
569
570 // Note: It's not necessary to clean up the current_build or other
571 // pointers at this point because we're guaranteed to be in an error
572 // state already so a user error callback can't longjmp out. This
573 // destroy function will complete no matter what so it doesn't matter
574 // what junk is left in the writer.
575 }
576 #endif
577
578 // flush any outstanding data
579 if (mpack_writer_error(writer) == mpack_ok && mpack_writer_buffer_used(writer) != 0 && writer->flush != NULL) {
580 writer->flush(writer, writer->buffer, mpack_writer_buffer_used(writer));
581 writer->flush = NULL;
582 }
583
584 if (writer->teardown) {
585 writer->teardown(writer);
586 writer->teardown = NULL;
587 }
588
589 return writer->error;
590}
591
592void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t value) {
593 switch (value.type) {
594 case mpack_type_missing:
595 mpack_break("cannot write a missing value!");
596 mpack_writer_flag_error(writer, mpack_error_bug);
597 return;
598
599 case mpack_type_nil: mpack_write_nil (writer); return;
600 case mpack_type_bool: mpack_write_bool (writer, value.v.b); return;
601 case mpack_type_int: mpack_write_int (writer, value.v.i); return;
602 case mpack_type_uint: mpack_write_uint (writer, value.v.u); return;
603
604 case mpack_type_float:
605 #if MPACK_FLOAT
606 mpack_write_float
607 #else
608 mpack_write_raw_float
609 #endif
610 (writer, value.v.f);
611 return;
612 case mpack_type_double:
613 #if MPACK_DOUBLE
614 mpack_write_double
615 #else
616 mpack_write_raw_double
617 #endif
618 (writer, value.v.d);
619 return;
620
621 case mpack_type_str: mpack_start_str(writer, value.v.l); return;
622 case mpack_type_bin: mpack_start_bin(writer, value.v.l); return;
623
624 #if MPACK_EXTENSIONS
625 case mpack_type_ext:
626 mpack_start_ext(writer, mpack_tag_ext_exttype(&value), mpack_tag_ext_length(&value));
627 return;
628 #endif
629
630 case mpack_type_array: mpack_start_array(writer, value.v.n); return;
631 case mpack_type_map: mpack_start_map(writer, value.v.n); return;
632 }
633
634 mpack_break("unrecognized type %i", (int)value.type);
635 mpack_writer_flag_error(writer, mpack_error_bug);
636}
637
638MPACK_STATIC_INLINE void mpack_write_byte_element(mpack_writer_t* writer, char value) {
639 mpack_writer_track_element(writer);
640 if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= 1) || mpack_writer_ensure(writer, 1))
641 *(writer->position++) = value;
642}
643
644void mpack_write_nil(mpack_writer_t* writer) {
645 mpack_write_byte_element(writer, (char)0xc0);
646}
647
648void mpack_write_bool(mpack_writer_t* writer, bool value) {
649 mpack_write_byte_element(writer, (char)(0xc2 | (value ? 1 : 0)));
650}
651
652void mpack_write_true(mpack_writer_t* writer) {
653 mpack_write_byte_element(writer, (char)0xc3);
654}
655
656void mpack_write_false(mpack_writer_t* writer) {
657 mpack_write_byte_element(writer, (char)0xc2);
658}
659
660void mpack_write_object_bytes(mpack_writer_t* writer, const char* data, size_t bytes) {
661 mpack_writer_track_element(writer);
662 mpack_write_native(writer, data, bytes);
663}
664
665/*
666 * Encode functions
667 */
668
669MPACK_STATIC_INLINE void mpack_encode_fixuint(char* p, uint8_t value) {
670 mpack_assert(value <= 127);
671 mpack_store_u8(p, value);
672}
673
674MPACK_STATIC_INLINE void mpack_encode_u8(char* p, uint8_t value) {
675 mpack_assert(value > 127);
676 mpack_store_u8(p, 0xcc);
677 mpack_store_u8(p + 1, value);
678}
679
680MPACK_STATIC_INLINE void mpack_encode_u16(char* p, uint16_t value) {
681 mpack_assert(value > MPACK_UINT8_MAX);
682 mpack_store_u8(p, 0xcd);
683 mpack_store_u16(p + 1, value);
684}
685
686MPACK_STATIC_INLINE void mpack_encode_u32(char* p, uint32_t value) {
687 mpack_assert(value > MPACK_UINT16_MAX);
688 mpack_store_u8(p, 0xce);
689 mpack_store_u32(p + 1, value);
690}
691
692MPACK_STATIC_INLINE void mpack_encode_u64(char* p, uint64_t value) {
693 mpack_assert(value > MPACK_UINT32_MAX);
694 mpack_store_u8(p, 0xcf);
695 mpack_store_u64(p + 1, value);
696}
697
698MPACK_STATIC_INLINE void mpack_encode_fixint(char* p, int8_t value) {
699 // this can encode positive or negative fixints
700 mpack_assert(value >= -32);
701 mpack_store_i8(p, value);
702}
703
704MPACK_STATIC_INLINE void mpack_encode_i8(char* p, int8_t value) {
705 mpack_assert(value < -32);
706 mpack_store_u8(p, 0xd0);
707 mpack_store_i8(p + 1, value);
708}
709
710MPACK_STATIC_INLINE void mpack_encode_i16(char* p, int16_t value) {
711 mpack_assert(value < MPACK_INT8_MIN);
712 mpack_store_u8(p, 0xd1);
713 mpack_store_i16(p + 1, value);
714}
715
716MPACK_STATIC_INLINE void mpack_encode_i32(char* p, int32_t value) {
717 mpack_assert(value < MPACK_INT16_MIN);
718 mpack_store_u8(p, 0xd2);
719 mpack_store_i32(p + 1, value);
720}
721
722MPACK_STATIC_INLINE void mpack_encode_i64(char* p, int64_t value) {
723 mpack_assert(value < MPACK_INT32_MIN);
724 mpack_store_u8(p, 0xd3);
725 mpack_store_i64(p + 1, value);
726}
727
728#if MPACK_FLOAT
729MPACK_STATIC_INLINE void mpack_encode_float(char* p, float value) {
730 mpack_store_u8(p, 0xca);
731 mpack_store_float(p + 1, value);
732}
733#else
734MPACK_STATIC_INLINE void mpack_encode_raw_float(char* p, uint32_t value) {
735 mpack_store_u8(p, 0xca);
736 mpack_store_u32(p + 1, value);
737}
738#endif
739
740#if MPACK_DOUBLE
741MPACK_STATIC_INLINE void mpack_encode_double(char* p, double value) {
742 mpack_store_u8(p, 0xcb);
743 mpack_store_double(p + 1, value);
744}
745#else
746MPACK_STATIC_INLINE void mpack_encode_raw_double(char* p, uint64_t value) {
747 mpack_store_u8(p, 0xcb);
748 mpack_store_u64(p + 1, value);
749}
750#endif
751
752MPACK_STATIC_INLINE void mpack_encode_fixarray(char* p, uint8_t count) {
753 mpack_assert(count <= 15);
754 mpack_store_u8(p, (uint8_t)(0x90 | count));
755}
756
757MPACK_STATIC_INLINE void mpack_encode_array16(char* p, uint16_t count) {
758 mpack_assert(count > 15);
759 mpack_store_u8(p, 0xdc);
760 mpack_store_u16(p + 1, count);
761}
762
763MPACK_STATIC_INLINE void mpack_encode_array32(char* p, uint32_t count) {
764 mpack_assert(count > MPACK_UINT16_MAX);
765 mpack_store_u8(p, 0xdd);
766 mpack_store_u32(p + 1, count);
767}
768
769MPACK_STATIC_INLINE void mpack_encode_fixmap(char* p, uint8_t count) {
770 mpack_assert(count <= 15);
771 mpack_store_u8(p, (uint8_t)(0x80 | count));
772}
773
774MPACK_STATIC_INLINE void mpack_encode_map16(char* p, uint16_t count) {
775 mpack_assert(count > 15);
776 mpack_store_u8(p, 0xde);
777 mpack_store_u16(p + 1, count);
778}
779
780MPACK_STATIC_INLINE void mpack_encode_map32(char* p, uint32_t count) {
781 mpack_assert(count > MPACK_UINT16_MAX);
782 mpack_store_u8(p, 0xdf);
783 mpack_store_u32(p + 1, count);
784}
785
786MPACK_STATIC_INLINE void mpack_encode_fixstr(char* p, uint8_t count) {
787 mpack_assert(count <= 31);
788 mpack_store_u8(p, (uint8_t)(0xa0 | count));
789}
790
791MPACK_STATIC_INLINE void mpack_encode_str8(char* p, uint8_t count) {
792 mpack_assert(count > 31);
793 mpack_store_u8(p, 0xd9);
794 mpack_store_u8(p + 1, count);
795}
796
797MPACK_STATIC_INLINE void mpack_encode_str16(char* p, uint16_t count) {
798 // we might be encoding a raw in compatibility mode, so we
799 // allow count to be in the range [32, MPACK_UINT8_MAX].
800 mpack_assert(count > 31);
801 mpack_store_u8(p, 0xda);
802 mpack_store_u16(p + 1, count);
803}
804
805MPACK_STATIC_INLINE void mpack_encode_str32(char* p, uint32_t count) {
806 mpack_assert(count > MPACK_UINT16_MAX);
807 mpack_store_u8(p, 0xdb);
808 mpack_store_u32(p + 1, count);
809}
810
811MPACK_STATIC_INLINE void mpack_encode_bin8(char* p, uint8_t count) {
812 mpack_store_u8(p, 0xc4);
813 mpack_store_u8(p + 1, count);
814}
815
816MPACK_STATIC_INLINE void mpack_encode_bin16(char* p, uint16_t count) {
817 mpack_assert(count > MPACK_UINT8_MAX);
818 mpack_store_u8(p, 0xc5);
819 mpack_store_u16(p + 1, count);
820}
821
822MPACK_STATIC_INLINE void mpack_encode_bin32(char* p, uint32_t count) {
823 mpack_assert(count > MPACK_UINT16_MAX);
824 mpack_store_u8(p, 0xc6);
825 mpack_store_u32(p + 1, count);
826}
827
828#if MPACK_EXTENSIONS
829MPACK_STATIC_INLINE void mpack_encode_fixext1(char* p, int8_t exttype) {
830 mpack_store_u8(p, 0xd4);
831 mpack_store_i8(p + 1, exttype);
832}
833
834MPACK_STATIC_INLINE void mpack_encode_fixext2(char* p, int8_t exttype) {
835 mpack_store_u8(p, 0xd5);
836 mpack_store_i8(p + 1, exttype);
837}
838
839MPACK_STATIC_INLINE void mpack_encode_fixext4(char* p, int8_t exttype) {
840 mpack_store_u8(p, 0xd6);
841 mpack_store_i8(p + 1, exttype);
842}
843
844MPACK_STATIC_INLINE void mpack_encode_fixext8(char* p, int8_t exttype) {
845 mpack_store_u8(p, 0xd7);
846 mpack_store_i8(p + 1, exttype);
847}
848
849MPACK_STATIC_INLINE void mpack_encode_fixext16(char* p, int8_t exttype) {
850 mpack_store_u8(p, 0xd8);
851 mpack_store_i8(p + 1, exttype);
852}
853
854MPACK_STATIC_INLINE void mpack_encode_ext8(char* p, int8_t exttype, uint8_t count) {
855 mpack_assert(count != 1 && count != 2 && count != 4 && count != 8 && count != 16);
856 mpack_store_u8(p, 0xc7);
857 mpack_store_u8(p + 1, count);
858 mpack_store_i8(p + 2, exttype);
859}
860
861MPACK_STATIC_INLINE void mpack_encode_ext16(char* p, int8_t exttype, uint16_t count) {
862 mpack_assert(count > MPACK_UINT8_MAX);
863 mpack_store_u8(p, 0xc8);
864 mpack_store_u16(p + 1, count);
865 mpack_store_i8(p + 3, exttype);
866}
867
868MPACK_STATIC_INLINE void mpack_encode_ext32(char* p, int8_t exttype, uint32_t count) {
869 mpack_assert(count > MPACK_UINT16_MAX);
870 mpack_store_u8(p, 0xc9);
871 mpack_store_u32(p + 1, count);
872 mpack_store_i8(p + 5, exttype);
873}
874
875MPACK_STATIC_INLINE void mpack_encode_timestamp_4(char* p, uint32_t seconds) {
876 mpack_encode_fixext4(p, MPACK_EXTTYPE_TIMESTAMP);
877 mpack_store_u32(p + MPACK_TAG_SIZE_FIXEXT4, seconds);
878}
879
880MPACK_STATIC_INLINE void mpack_encode_timestamp_8(char* p, int64_t seconds, uint32_t nanoseconds) {
881 mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
882 mpack_encode_fixext8(p, MPACK_EXTTYPE_TIMESTAMP);
883 uint64_t encoded = ((uint64_t)nanoseconds << 34) | (uint64_t)seconds;
884 mpack_store_u64(p + MPACK_TAG_SIZE_FIXEXT8, encoded);
885}
886
887MPACK_STATIC_INLINE void mpack_encode_timestamp_12(char* p, int64_t seconds, uint32_t nanoseconds) {
888 mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
889 mpack_encode_ext8(p, MPACK_EXTTYPE_TIMESTAMP, 12);
890 mpack_store_u32(p + MPACK_TAG_SIZE_EXT8, nanoseconds);
891 mpack_store_i64(p + MPACK_TAG_SIZE_EXT8 + 4, seconds);
892}
893#endif
894
895
896
897/*
898 * Write functions
899 */
900
901// This is a macro wrapper to the encode functions to encode
902// directly into the buffer. If mpack_writer_ensure() fails
903// it will flag an error so we don't have to do anything.
904#define MPACK_WRITE_ENCODED(encode_fn, size, ...) do { \
905 if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) { \
906 MPACK_EXPAND(encode_fn(writer->position, __VA_ARGS__)); \
907 writer->position += size; \
908 } \
909} while (0)
910
911void mpack_write_u8(mpack_writer_t* writer, uint8_t value) {
912 #if MPACK_OPTIMIZE_FOR_SIZE
913 mpack_write_u64(writer, value);
914 #else
915 mpack_writer_track_element(writer);
916 if (value <= 127) {
917 MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, value);
918 } else {
919 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, value);
920 }
921 #endif
922}
923
924void mpack_write_u16(mpack_writer_t* writer, uint16_t value) {
925 #if MPACK_OPTIMIZE_FOR_SIZE
926 mpack_write_u64(writer, value);
927 #else
928 mpack_writer_track_element(writer);
929 if (value <= 127) {
930 MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
931 } else if (value <= MPACK_UINT8_MAX) {
932 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
933 } else {
934 MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, value);
935 }
936 #endif
937}
938
939void mpack_write_u32(mpack_writer_t* writer, uint32_t value) {
940 #if MPACK_OPTIMIZE_FOR_SIZE
941 mpack_write_u64(writer, value);
942 #else
943 mpack_writer_track_element(writer);
944 if (value <= 127) {
945 MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
946 } else if (value <= MPACK_UINT8_MAX) {
947 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
948 } else if (value <= MPACK_UINT16_MAX) {
949 MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
950 } else {
951 MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, value);
952 }
953 #endif
954}
955
956void mpack_write_u64(mpack_writer_t* writer, uint64_t value) {
957 mpack_writer_track_element(writer);
958
959 if (value <= 127) {
960 MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
961 } else if (value <= MPACK_UINT8_MAX) {
962 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
963 } else if (value <= MPACK_UINT16_MAX) {
964 MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
965 } else if (value <= MPACK_UINT32_MAX) {
966 MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
967 } else {
968 MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, value);
969 }
970}
971
972void mpack_write_i8(mpack_writer_t* writer, int8_t value) {
973 #if MPACK_OPTIMIZE_FOR_SIZE
974 mpack_write_i64(writer, value);
975 #else
976 mpack_writer_track_element(writer);
977 if (value >= -32) {
978 // we encode positive and negative fixints together
979 MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
980 } else {
981 MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
982 }
983 #endif
984}
985
986void mpack_write_i16(mpack_writer_t* writer, int16_t value) {
987 #if MPACK_OPTIMIZE_FOR_SIZE
988 mpack_write_i64(writer, value);
989 #else
990 mpack_writer_track_element(writer);
991 if (value >= -32) {
992 if (value <= 127) {
993 // we encode positive and negative fixints together
994 MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
995 } else if (value <= MPACK_UINT8_MAX) {
996 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
997 } else {
998 MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
999 }
1000 } else if (value >= MPACK_INT8_MIN) {
1001 MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
1002 } else {
1003 MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
1004 }
1005 #endif
1006}
1007
1008void mpack_write_i32(mpack_writer_t* writer, int32_t value) {
1009 #if MPACK_OPTIMIZE_FOR_SIZE
1010 mpack_write_i64(writer, value);
1011 #else
1012 mpack_writer_track_element(writer);
1013 if (value >= -32) {
1014 if (value <= 127) {
1015 // we encode positive and negative fixints together
1016 MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
1017 } else if (value <= MPACK_UINT8_MAX) {
1018 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
1019 } else if (value <= MPACK_UINT16_MAX) {
1020 MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
1021 } else {
1022 MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
1023 }
1024 } else if (value >= MPACK_INT8_MIN) {
1025 MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
1026 } else if (value >= MPACK_INT16_MIN) {
1027 MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
1028 } else {
1029 MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, value);
1030 }
1031 #endif
1032}
1033
1034void mpack_write_i64(mpack_writer_t* writer, int64_t value) {
1035 #if MPACK_OPTIMIZE_FOR_SIZE
1036 if (value > 127) {
1037 // for non-fix positive ints we call the u64 writer to save space
1038 mpack_write_u64(writer, (uint64_t)value);
1039 return;
1040 }
1041 #endif
1042
1043 mpack_writer_track_element(writer);
1044 if (value >= -32) {
1045 #if MPACK_OPTIMIZE_FOR_SIZE
1046 MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
1047 #else
1048 if (value <= 127) {
1049 MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
1050 } else if (value <= MPACK_UINT8_MAX) {
1051 MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
1052 } else if (value <= MPACK_UINT16_MAX) {
1053 MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
1054 } else if (value <= MPACK_UINT32_MAX) {
1055 MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
1056 } else {
1057 MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, (uint64_t)value);
1058 }
1059 #endif
1060 } else if (value >= MPACK_INT8_MIN) {
1061 MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
1062 } else if (value >= MPACK_INT16_MIN) {
1063 MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
1064 } else if (value >= MPACK_INT32_MIN) {
1065 MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, (int32_t)value);
1066 } else {
1067 MPACK_WRITE_ENCODED(mpack_encode_i64, MPACK_TAG_SIZE_I64, value);
1068 }
1069}
1070
1071#if MPACK_FLOAT
1072void mpack_write_float(mpack_writer_t* writer, float value) {
1073 mpack_writer_track_element(writer);
1074 MPACK_WRITE_ENCODED(mpack_encode_float, MPACK_TAG_SIZE_FLOAT, value);
1075}
1076#else
1077void mpack_write_raw_float(mpack_writer_t* writer, uint32_t value) {
1078 mpack_writer_track_element(writer);
1079 MPACK_WRITE_ENCODED(mpack_encode_raw_float, MPACK_TAG_SIZE_FLOAT, value);
1080}
1081#endif
1082
1083#if MPACK_DOUBLE
1084void mpack_write_double(mpack_writer_t* writer, double value) {
1085 mpack_writer_track_element(writer);
1086 MPACK_WRITE_ENCODED(mpack_encode_double, MPACK_TAG_SIZE_DOUBLE, value);
1087}
1088#else
1089void mpack_write_raw_double(mpack_writer_t* writer, uint64_t value) {
1090 mpack_writer_track_element(writer);
1091 MPACK_WRITE_ENCODED(mpack_encode_raw_double, MPACK_TAG_SIZE_DOUBLE, value);
1092}
1093#endif
1094
1095#if MPACK_EXTENSIONS
1096void mpack_write_timestamp(mpack_writer_t* writer, int64_t seconds, uint32_t nanoseconds) {
1097 #if MPACK_COMPATIBILITY
1098 if (writer->version <= mpack_version_v4) {
1099 mpack_break("Timestamps require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
1100 mpack_writer_flag_error(writer, mpack_error_bug);
1101 return;
1102 }
1103 #endif
1104
1105 if (nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
1106 mpack_break("timestamp nanoseconds out of bounds: %" PRIu32 , nanoseconds);
1107 mpack_writer_flag_error(writer, mpack_error_bug);
1108 return;
1109 }
1110
1111 mpack_writer_track_element(writer);
1112
1113 if (seconds < 0 || seconds >= (MPACK_INT64_C(1) << 34)) {
1114 MPACK_WRITE_ENCODED(mpack_encode_timestamp_12, MPACK_EXT_SIZE_TIMESTAMP12, seconds, nanoseconds);
1115 } else if (seconds > MPACK_UINT32_MAX || nanoseconds > 0) {
1116 MPACK_WRITE_ENCODED(mpack_encode_timestamp_8, MPACK_EXT_SIZE_TIMESTAMP8, seconds, nanoseconds);
1117 } else {
1118 MPACK_WRITE_ENCODED(mpack_encode_timestamp_4, MPACK_EXT_SIZE_TIMESTAMP4, (uint32_t)seconds);
1119 }
1120}
1121#endif
1122
1123static void mpack_write_array_notrack(mpack_writer_t* writer, uint32_t count) {
1124 if (count <= 15) {
1125 MPACK_WRITE_ENCODED(mpack_encode_fixarray, MPACK_TAG_SIZE_FIXARRAY, (uint8_t)count);
1126 } else if (count <= MPACK_UINT16_MAX) {
1127 MPACK_WRITE_ENCODED(mpack_encode_array16, MPACK_TAG_SIZE_ARRAY16, (uint16_t)count);
1128 } else {
1129 MPACK_WRITE_ENCODED(mpack_encode_array32, MPACK_TAG_SIZE_ARRAY32, (uint32_t)count);
1130 }
1131}
1132
1133static void mpack_write_map_notrack(mpack_writer_t* writer, uint32_t count) {
1134 if (count <= 15) {
1135 MPACK_WRITE_ENCODED(mpack_encode_fixmap, MPACK_TAG_SIZE_FIXMAP, (uint8_t)count);
1136 } else if (count <= MPACK_UINT16_MAX) {
1137 MPACK_WRITE_ENCODED(mpack_encode_map16, MPACK_TAG_SIZE_MAP16, (uint16_t)count);
1138 } else {
1139 MPACK_WRITE_ENCODED(mpack_encode_map32, MPACK_TAG_SIZE_MAP32, (uint32_t)count);
1140 }
1141}
1142
1143void mpack_start_array(mpack_writer_t* writer, uint32_t count) {
1144 mpack_writer_track_element(writer);
1145 mpack_write_array_notrack(writer, count);
1146 mpack_writer_track_push(writer, mpack_type_array, count);
1147 mpack_builder_compound_push(writer);
1148}
1149
1150void mpack_start_map(mpack_writer_t* writer, uint32_t count) {
1151 mpack_writer_track_element(writer);
1152 mpack_write_map_notrack(writer, count);
1153 mpack_writer_track_push(writer, mpack_type_map, count);
1154 mpack_builder_compound_push(writer);
1155}
1156
1157static void mpack_start_str_notrack(mpack_writer_t* writer, uint32_t count) {
1158 if (count <= 31) {
1159 MPACK_WRITE_ENCODED(mpack_encode_fixstr, MPACK_TAG_SIZE_FIXSTR, (uint8_t)count);
1160
1161 // str8 is only supported in v5 or later.
1162 } else if (count <= MPACK_UINT8_MAX
1163 #if MPACK_COMPATIBILITY
1164 && writer->version >= mpack_version_v5
1165 #endif
1166 ) {
1167 MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
1168
1169 } else if (count <= MPACK_UINT16_MAX) {
1170 MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
1171 } else {
1172 MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
1173 }
1174}
1175
1176static void mpack_start_bin_notrack(mpack_writer_t* writer, uint32_t count) {
1177 #if MPACK_COMPATIBILITY
1178 // In the v4 spec, there was only the raw type for any kind of
1179 // variable-length data. In v4 mode, we support the bin functions,
1180 // but we produce an old-style raw.
1181 if (writer->version <= mpack_version_v4) {
1182 mpack_start_str_notrack(writer, count);
1183 return;
1184 }
1185 #endif
1186
1187 if (count <= MPACK_UINT8_MAX) {
1188 MPACK_WRITE_ENCODED(mpack_encode_bin8, MPACK_TAG_SIZE_BIN8, (uint8_t)count);
1189 } else if (count <= MPACK_UINT16_MAX) {
1190 MPACK_WRITE_ENCODED(mpack_encode_bin16, MPACK_TAG_SIZE_BIN16, (uint16_t)count);
1191 } else {
1192 MPACK_WRITE_ENCODED(mpack_encode_bin32, MPACK_TAG_SIZE_BIN32, (uint32_t)count);
1193 }
1194}
1195
1196void mpack_start_str(mpack_writer_t* writer, uint32_t count) {
1197 mpack_writer_track_element(writer);
1198 mpack_start_str_notrack(writer, count);
1199 mpack_writer_track_push(writer, mpack_type_str, count);
1200}
1201
1202void mpack_start_bin(mpack_writer_t* writer, uint32_t count) {
1203 mpack_writer_track_element(writer);
1204 mpack_start_bin_notrack(writer, count);
1205 mpack_writer_track_push(writer, mpack_type_bin, count);
1206}
1207
1208#if MPACK_EXTENSIONS
1209void mpack_start_ext(mpack_writer_t* writer, int8_t exttype, uint32_t count) {
1210 #if MPACK_COMPATIBILITY
1211 if (writer->version <= mpack_version_v4) {
1212 mpack_break("Ext types require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
1213 mpack_writer_flag_error(writer, mpack_error_bug);
1214 return;
1215 }
1216 #endif
1217
1218 mpack_writer_track_element(writer);
1219
1220 if (count == 1) {
1221 MPACK_WRITE_ENCODED(mpack_encode_fixext1, MPACK_TAG_SIZE_FIXEXT1, exttype);
1222 } else if (count == 2) {
1223 MPACK_WRITE_ENCODED(mpack_encode_fixext2, MPACK_TAG_SIZE_FIXEXT2, exttype);
1224 } else if (count == 4) {
1225 MPACK_WRITE_ENCODED(mpack_encode_fixext4, MPACK_TAG_SIZE_FIXEXT4, exttype);
1226 } else if (count == 8) {
1227 MPACK_WRITE_ENCODED(mpack_encode_fixext8, MPACK_TAG_SIZE_FIXEXT8, exttype);
1228 } else if (count == 16) {
1229 MPACK_WRITE_ENCODED(mpack_encode_fixext16, MPACK_TAG_SIZE_FIXEXT16, exttype);
1230 } else if (count <= MPACK_UINT8_MAX) {
1231 MPACK_WRITE_ENCODED(mpack_encode_ext8, MPACK_TAG_SIZE_EXT8, exttype, (uint8_t)count);
1232 } else if (count <= MPACK_UINT16_MAX) {
1233 MPACK_WRITE_ENCODED(mpack_encode_ext16, MPACK_TAG_SIZE_EXT16, exttype, (uint16_t)count);
1234 } else {
1235 MPACK_WRITE_ENCODED(mpack_encode_ext32, MPACK_TAG_SIZE_EXT32, exttype, (uint32_t)count);
1236 }
1237
1238 mpack_writer_track_push(writer, mpack_type_ext, count);
1239}
1240#endif
1241
1242
1243
1244/*
1245 * Compound helpers and other functions
1246 */
1247
1248void mpack_write_str(mpack_writer_t* writer, const char* data, uint32_t count) {
1249 mpack_assert(count == 0 || data != NULL, "data for string of length %i is NULL", (int)count);
1250
1251 #if MPACK_OPTIMIZE_FOR_SIZE
1252 mpack_writer_track_element(writer);
1253 mpack_start_str_notrack(writer, count);
1254 mpack_write_native(writer, data, count);
1255 #else
1256
1257 mpack_writer_track_element(writer);
1258
1259 if (count <= 31) {
1260 // The minimum buffer size when using a flush function is guaranteed to
1261 // fit the largest possible fixstr.
1262 size_t size = count + MPACK_TAG_SIZE_FIXSTR;
1263 if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) {
1264 char* MPACK_RESTRICT p = writer->position;
1265 mpack_encode_fixstr(p, (uint8_t)count);
1266 mpack_memcpy(p + MPACK_TAG_SIZE_FIXSTR, data, count);
1267 writer->position += count + MPACK_TAG_SIZE_FIXSTR;
1268 }
1269 return;
1270 }
1271
1272 if (count <= MPACK_UINT8_MAX
1273 #if MPACK_COMPATIBILITY
1274 && writer->version >= mpack_version_v5
1275 #endif
1276 ) {
1277 if (count + MPACK_TAG_SIZE_STR8 <= mpack_writer_buffer_left(writer)) {
1278 char* MPACK_RESTRICT p = writer->position;
1279 mpack_encode_str8(p, (uint8_t)count);
1280 mpack_memcpy(p + MPACK_TAG_SIZE_STR8, data, count);
1281 writer->position += count + MPACK_TAG_SIZE_STR8;
1282 } else {
1283 MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
1284 mpack_write_native(writer, data, count);
1285 }
1286 return;
1287 }
1288
1289 // str16 and str32 are likely to be a significant fraction of the buffer
1290 // size, so we don't bother with a combined space check in order to
1291 // minimize code size.
1292 if (count <= MPACK_UINT16_MAX) {
1293 MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
1294 mpack_write_native(writer, data, count);
1295 } else {
1296 MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
1297 mpack_write_native(writer, data, count);
1298 }
1299
1300 #endif
1301}
1302
1303void mpack_write_bin(mpack_writer_t* writer, const char* data, uint32_t count) {
1304 mpack_assert(count == 0 || data != NULL, "data pointer for bin of %i bytes is NULL", (int)count);
1305 mpack_start_bin(writer, count);
1306 mpack_write_bytes(writer, data, count);
1307 mpack_finish_bin(writer);
1308}
1309
1310#if MPACK_EXTENSIONS
1311void mpack_write_ext(mpack_writer_t* writer, int8_t exttype, const char* data, uint32_t count) {
1312 mpack_assert(count == 0 || data != NULL, "data pointer for ext of type %i and %i bytes is NULL", exttype, (int)count);
1313 mpack_start_ext(writer, exttype, count);
1314 mpack_write_bytes(writer, data, count);
1315 mpack_finish_ext(writer);
1316}
1317#endif
1318
1319void mpack_write_bytes(mpack_writer_t* writer, const char* data, size_t count) {
1320 mpack_assert(count == 0 || data != NULL, "data pointer for %i bytes is NULL", (int)count);
1321 mpack_writer_track_bytes(writer, count);
1322 mpack_write_native(writer, data, count);
1323}
1324
1325void mpack_write_cstr(mpack_writer_t* writer, const char* cstr) {
1326 mpack_assert(cstr != NULL, "cstr pointer is NULL");
1327 size_t length = mpack_strlen(cstr);
1328 if (length > MPACK_UINT32_MAX)
1329 mpack_writer_flag_error(writer, mpack_error_invalid);
1330 mpack_write_str(writer, cstr, (uint32_t)length);
1331}
1332
1333void mpack_write_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
1334 if (cstr)
1335 mpack_write_cstr(writer, cstr);
1336 else
1337 mpack_write_nil(writer);
1338}
1339
1340void mpack_write_utf8(mpack_writer_t* writer, const char* str, uint32_t length) {
1341 mpack_assert(length == 0 || str != NULL, "data for string of length %i is NULL", (int)length);
1342 if (!mpack_utf8_check(str, length)) {
1343 mpack_writer_flag_error(writer, mpack_error_invalid);
1344 return;
1345 }
1346 mpack_write_str(writer, str, length);
1347}
1348
1349void mpack_write_utf8_cstr(mpack_writer_t* writer, const char* cstr) {
1350 mpack_assert(cstr != NULL, "cstr pointer is NULL");
1351 size_t length = mpack_strlen(cstr);
1352 if (length > MPACK_UINT32_MAX) {
1353 mpack_writer_flag_error(writer, mpack_error_invalid);
1354 return;
1355 }
1356 mpack_write_utf8(writer, cstr, (uint32_t)length);
1357}
1358
1359void mpack_write_utf8_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
1360 if (cstr)
1361 mpack_write_utf8_cstr(writer, cstr);
1362 else
1363 mpack_write_nil(writer);
1364}
1365
1366/*
1367 * Builder implementation
1368 *
1369 * When a writer is in build mode, it diverts writes to an internal growable
1370 * buffer. All elements other than builder start tags are encoded as normal
1371 * into the builder buffer (even nested maps and arrays of known size, e.g.
1372 * `mpack_start_array()`.) But for compound elements of unknown size, an
1373 * mpack_build_t is written to the buffer instead.
1374 *
1375 * The mpack_build_t tracks everything needed to re-constitute the final
1376 * message once all sizes are known. When the last build element is completed,
1377 * the builder resolves the build by walking through the builds, outputting the
1378 * final encoded tag, and copying everything in between to the writer's true
1379 * buffer.
1380 *
1381 * To make things extra complicated, the builder buffer is not contiguous. It's
1382 * allocated in pages, where the first page may be an internal page in the
1383 * writer. But, each mpack_build_t must itself be contiguous and aligned
1384 * properly within the buffer. This means bytes can be skipped (and wasted)
1385 * before the builds or at the end of pages.
1386 *
1387 * To keep track of this, builds store both their element count and the number
1388 * of encoded bytes that follow, and pages store the number of bytes used. As
1389 * elements are written, each element adds to the count in the current open
1390 * build, and the number of bytes written adds to the current page and the byte
1391 * count in the last started build (whether or not it is completed.)
1392 */
1393
1394#if MPACK_BUILDER
1395
1396#ifdef MPACK_ALIGNOF
1397 #define MPACK_BUILD_ALIGNMENT MPACK_ALIGNOF(mpack_build_t)
1398#else
1399 // without alignof, we just align to the greater of size_t, void* and uint64_t.
1400 // (we do this even though we don't have uint64_t in it in case we add it later.)
1401 #define MPACK_BUILD_ALIGNMENT_MAX(x, y) ((x) > (y) ? (x) : (y))
1402 #define MPACK_BUILD_ALIGNMENT (MPACK_BUILD_ALIGNMENT_MAX(sizeof(void*), \
1403 MPACK_BUILD_ALIGNMENT_MAX(sizeof(size_t), sizeof(uint64_t))))
1404#endif
1405
1406static inline void mpack_builder_check_sizes(mpack_writer_t* writer) {
1407
1408 // We check internal and page sizes here so that we don't have to check
1409 // them again. A new page with a build in it will have a page header,
1410 // build, and minimum space for a tag. This will perform horribly and waste
1411 // tons of memory if the page size is small, so you're best off just
1412 // sticking with the defaults.
1413 //
1414 // These are all known at compile time, so if they are large
1415 // enough this function should trivially optimize to a no-op.
1416
1417 #if MPACK_BUILDER_INTERNAL_STORAGE
1418 // make sure the internal storage is big enough to be useful
1419 MPACK_STATIC_ASSERT(MPACK_BUILDER_INTERNAL_STORAGE_SIZE >= (sizeof(mpack_builder_page_t) +
1420 sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE),
1421 "MPACK_BUILDER_INTERNAL_STORAGE_SIZE is too small to be useful!");
1422 if (MPACK_BUILDER_INTERNAL_STORAGE_SIZE < (sizeof(mpack_builder_page_t) +
1423 sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE))
1424 {
1425 mpack_break("MPACK_BUILDER_INTERNAL_STORAGE_SIZE is too small to be useful!");
1426 mpack_writer_flag_error(writer, mpack_error_bug);
1427 }
1428 #endif
1429
1430 // make sure the builder page size is big enough to be useful
1431 MPACK_STATIC_ASSERT(MPACK_BUILDER_PAGE_SIZE >= (sizeof(mpack_builder_page_t) +
1432 sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE),
1433 "MPACK_BUILDER_PAGE_SIZE is too small to be useful!");
1434 if (MPACK_BUILDER_PAGE_SIZE < (sizeof(mpack_builder_page_t) +
1435 sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE))
1436 {
1437 mpack_break("MPACK_BUILDER_PAGE_SIZE is too small to be useful!");
1438 mpack_writer_flag_error(writer, mpack_error_bug);
1439 }
1440}
1441
1442static inline size_t mpack_builder_page_size(mpack_writer_t* writer, mpack_builder_page_t* page) {
1443 #if MPACK_BUILDER_INTERNAL_STORAGE
1444 if ((char*)page == writer->builder.internal)
1445 return sizeof(writer->builder.internal);
1446 #else
1447 (void)writer;
1448 (void)page;
1449 #endif
1450 return MPACK_BUILDER_PAGE_SIZE;
1451}
1452
1453static inline size_t mpack_builder_align_build(size_t bytes_used) {
1454 size_t offset = bytes_used;
1455 offset += MPACK_BUILD_ALIGNMENT - 1;
1456 offset -= offset % MPACK_BUILD_ALIGNMENT;
1457 mpack_log("aligned %zi to %zi\n", bytes_used, offset);
1458 return offset;
1459}
1460
1461static inline void mpack_builder_free_page(mpack_writer_t* writer, mpack_builder_page_t* page) {
1462 mpack_log("freeing page %p\n", (void*)page);
1463 #if MPACK_BUILDER_INTERNAL_STORAGE
1464 if ((char*)page == writer->builder.internal)
1465 return;
1466 #else
1467 (void)writer;
1468 #endif
1469 MPACK_FREE(page);
1470}
1471
1472static inline size_t mpack_builder_page_remaining(mpack_writer_t* writer, mpack_builder_page_t* page) {
1473 return mpack_builder_page_size(writer, page) - page->bytes_used;
1474}
1475
1476static void mpack_builder_configure_buffer(mpack_writer_t* writer) {
1477 if (mpack_writer_error(writer) != mpack_ok)
1478 return;
1479 mpack_builder_t* builder = &writer->builder;
1480
1481 mpack_builder_page_t* page = builder->current_page;
1482 mpack_assert(page != NULL, "page is null??");
1483
1484 // This diverts the writer into the remainder of the current page of our
1485 // build buffer.
1486 writer->buffer = (char*)page + page->bytes_used;
1487 writer->position = (char*)page + page->bytes_used;
1488 writer->end = (char*)page + mpack_builder_page_size(writer, page);
1489 mpack_log("configuring buffer from %p to %p\n", (void*)writer->position, (void*)writer->end);
1490}
1491
1492static void mpack_builder_add_page(mpack_writer_t* writer) {
1493 mpack_builder_t* builder = &writer->builder;
1494 mpack_assert(writer->error == mpack_ok);
1495
1496 mpack_log("adding a page.\n");
1497 mpack_builder_page_t* page = (mpack_builder_page_t*)MPACK_MALLOC(MPACK_BUILDER_PAGE_SIZE);
1498 if (page == NULL) {
1499 mpack_writer_flag_error(writer, mpack_error_memory);
1500 return;
1501 }
1502
1503 page->next = NULL;
1504 page->bytes_used = sizeof(mpack_builder_page_t);
1505 builder->current_page->next = page;
1506 builder->current_page = page;
1507}
1508
1509// Checks how many bytes the writer wrote to the page, adding it to the page's
1510// bytes_used. This must be followed up with mpack_builder_configure_buffer()
1511// (after adding a new page, build, etc) to reset the writer's buffer pointers.
1512static void mpack_builder_apply_writes(mpack_writer_t* writer) {
1513 mpack_assert(writer->error == mpack_ok);
1514 mpack_builder_t* builder = &writer->builder;
1515 mpack_log("latest build is %p\n", (void*)builder->latest_build);
1516
1517 // The difference between buffer and current is the number of bytes that
1518 // were written to the page.
1519 size_t bytes_written = (size_t)(writer->position - writer->buffer);
1520 mpack_log("applying write of %zi bytes to build %p\n", bytes_written, (void*)builder->latest_build);
1521
1522 mpack_assert(builder->current_page != NULL);
1523 mpack_assert(builder->latest_build != NULL);
1524 builder->current_page->bytes_used += bytes_written;
1525 builder->latest_build->bytes += bytes_written;
1526 mpack_log("latest build %p now has %zi bytes\n", (void*)builder->latest_build, builder->latest_build->bytes);
1527}
1528
1529static void mpack_builder_flush(mpack_writer_t* writer) {
1530 mpack_assert(writer->error == mpack_ok);
1531 mpack_builder_apply_writes(writer);
1532 mpack_builder_add_page(writer);
1533 mpack_builder_configure_buffer(writer);
1534}
1535
1536MPACK_NOINLINE static void mpack_builder_begin(mpack_writer_t* writer) {
1537 mpack_builder_t* builder = &writer->builder;
1538 mpack_assert(writer->error == mpack_ok);
1539 mpack_assert(builder->current_build == NULL);
1540 mpack_assert(builder->latest_build == NULL);
1541 mpack_assert(builder->pages == NULL);
1542
1543 // If this is the first build, we need to stash the real buffer backing our
1544 // writer. We'll be diverting the writer to our build buffer.
1545 builder->stash_buffer = writer->buffer;
1546 builder->stash_position = writer->position;
1547 builder->stash_end = writer->end;
1548
1549 mpack_builder_page_t* page;
1550
1551 // we've checked that both these sizes are large enough above.
1552 #if MPACK_BUILDER_INTERNAL_STORAGE
1553 page = (mpack_builder_page_t*)builder->internal;
1554 mpack_log("beginning builder with internal storage %p\n", (void*)page);
1555 #else
1556 page = (mpack_builder_page_t*)MPACK_MALLOC(MPACK_BUILDER_PAGE_SIZE);
1557 if (page == NULL) {
1558 mpack_writer_flag_error(writer, mpack_error_memory);
1559 return;
1560 }
1561 mpack_log("beginning builder with allocated page %p\n", (void*)page);
1562 #endif
1563
1564 page->next = NULL;
1565 page->bytes_used = sizeof(mpack_builder_page_t);
1566 builder->pages = page;
1567 builder->current_page = page;
1568}
1569
1570static void mpack_builder_build(mpack_writer_t* writer, mpack_type_t type) {
1571 mpack_builder_check_sizes(writer);
1572 if (mpack_writer_error(writer) != mpack_ok)
1573 return;
1574
1575 mpack_writer_track_element(writer);
1576 mpack_writer_track_push_builder(writer, type);
1577
1578 mpack_builder_t* builder = &writer->builder;
1579
1580 if (builder->current_build == NULL) {
1581 mpack_builder_begin(writer);
1582 } else {
1583 mpack_builder_apply_writes(writer);
1584 }
1585 if (mpack_writer_error(writer) != mpack_ok)
1586 return;
1587
1588 // find aligned space for a new build. if there isn't enough space in the
1589 // current page, we discard the remaining space in it and allocate a new
1590 // page.
1591 size_t offset = mpack_builder_align_build(builder->current_page->bytes_used);
1592 if (offset + sizeof(mpack_build_t) > mpack_builder_page_size(writer, builder->current_page)) {
1593 mpack_log("not enough space for a build. %zi bytes used of %zi in this page\n",
1594 builder->current_page->bytes_used, mpack_builder_page_size(writer, builder->current_page));
1595 mpack_builder_add_page(writer);
1596 // there is always enough space in a fresh page.
1597 offset = mpack_builder_align_build(builder->current_page->bytes_used);
1598 }
1599
1600 // allocate the build within the page. note that we don't keep track of the
1601 // space wasted due to the offset. instead the previous build has stored
1602 // how many bytes follow it, and we'll redo this offset calculation to find
1603 // this build after it.
1604 mpack_builder_page_t* page = builder->current_page;
1605 page->bytes_used = offset + sizeof(mpack_build_t);
1606 mpack_assert(page->bytes_used <= mpack_builder_page_size(writer, page));
1607 mpack_build_t* build = (mpack_build_t*)((char*)page + offset);
1608 mpack_log("created new build %p within page %p, which now has %zi bytes used\n",
1609 (void*)build, (void*)page, page->bytes_used);
1610
1611 // configure the new build
1612 build->parent = builder->current_build;
1613 build->bytes = 0;
1614 build->count = 0;
1615 build->type = type;
1616 build->key_needs_value = false;
1617 build->nested_compound_elements = 0;
1618
1619 mpack_log("setting current and latest build to new build %p\n", (void*)build);
1620 builder->current_build = build;
1621 builder->latest_build = build;
1622
1623 // we always need to provide a buffer that meets the minimum buffer size.
1624 // if there isn't enough space, we discard the remaining space in the
1625 // current page and allocate a new one.
1626 if (mpack_builder_page_remaining(writer, page) < MPACK_WRITER_MINIMUM_BUFFER_SIZE) {
1627 mpack_log("less than minimum buffer size in current page. %zi bytes used of %zi in this page\n",
1628 builder->current_page->bytes_used, mpack_builder_page_size(writer, builder->current_page));
1629 mpack_builder_add_page(writer);
1630 if (mpack_writer_error(writer) != mpack_ok)
1631 return;
1632 }
1633 mpack_assert(mpack_builder_page_remaining(writer, builder->current_page) >= MPACK_WRITER_MINIMUM_BUFFER_SIZE);
1634 mpack_builder_configure_buffer(writer);
1635}
1636
1637MPACK_NOINLINE
1638static void mpack_builder_resolve(mpack_writer_t* writer) {
1639 mpack_builder_t* builder = &writer->builder;
1640
1641 // We should not have gotten here if we are in an error state. If an error
1642 // occurs with an open builder, the writer will free the open builder pages
1643 // when destroyed.
1644 mpack_assert(mpack_writer_error(writer) == mpack_ok, "can't resolve in error state!");
1645
1646 // We don't want the user to longjmp out of any I/O errors while we are
1647 // walking the page list, so defer error callbacks to after we're done.
1648 mpack_writer_error_t error_fn = writer->error_fn;
1649 writer->error_fn = NULL;
1650
1651 // The starting page is the internal storage (if we have it), otherwise
1652 // it's the first page in the array
1653 mpack_builder_page_t* page =
1654 #if MPACK_BUILDER_INTERNAL_STORAGE
1655 (mpack_builder_page_t*)builder->internal
1656 #else
1657 builder->pages
1658 #endif
1659 ;
1660
1661 // We start by restoring the writer's original buffer so we can write the
1662 // data for real.
1663 writer->buffer = builder->stash_buffer;
1664 writer->position = builder->stash_position;
1665 writer->end = builder->stash_end;
1666
1667 // We can also close out the build now.
1668 builder->current_build = NULL;
1669 builder->latest_build = NULL;
1670 builder->current_page = NULL;
1671 builder->pages = NULL;
1672
1673 // the starting page always starts with the first build
1674 size_t offset = mpack_builder_align_build(sizeof(mpack_builder_page_t));
1675 mpack_build_t* build = (mpack_build_t*)((char*)page + offset);
1676 mpack_log("starting resolve with build %p in page %p\n", (void*)build, (void*)page);
1677
1678 // encoded data immediately follows the build
1679 offset += sizeof(mpack_build_t);
1680
1681 // Walk the list of builds, writing everything out in the buffer. Note that
1682 // we don't check for errors anywhere. The lower-level write functions will
1683 // all check for errors and do nothing after an error occurs. We need to
1684 // walk all pages anyway to free them, so there's not much point in
1685 // optimizing an error path at the expense of the normal path.
1686 while (true) {
1687
1688 // write out the container tag
1689 mpack_log("writing out an %s with count %" PRIu32 " followed by %zi bytes\n",
1690 mpack_type_to_string(build->type), build->count, build->bytes);
1691 switch (build->type) {
1692 case mpack_type_map:
1693 mpack_write_map_notrack(writer, build->count);
1694 break;
1695 case mpack_type_array:
1696 mpack_write_array_notrack(writer, build->count);
1697 break;
1698 default:
1699 mpack_break("invalid type in builder?");
1700 mpack_writer_flag_error(writer, mpack_error_bug);
1701 return;
1702 }
1703
1704 // figure out how many bytes follow this container. we're going to be
1705 // freeing pages as we write, so we need to be done with this build.
1706 size_t left = build->bytes;
1707 build = NULL;
1708
1709 // write out all bytes following this container
1710 while (left > 0) {
1711 size_t bytes_used = page->bytes_used;
1712 if (offset < bytes_used) {
1713 size_t step = bytes_used - offset;
1714 if (step > left)
1715 step = left;
1716 mpack_log("writing out %zi bytes starting at %p in page %p\n",
1717 step, (void*)((char*)page + offset), (void*)page);
1718 mpack_write_native(writer, (char*)page + offset, step);
1719 offset += step;
1720 left -= step;
1721 }
1722
1723 if (left == 0) {
1724 mpack_log("done writing bytes for this build\n");
1725 break;
1726 }
1727
1728 // still need to write more bytes. free this page and jump to the
1729 // next one.
1730 mpack_builder_page_t* next_page = page->next;
1731 mpack_builder_free_page(writer, page);
1732 page = next_page;
1733 // bytes on the next page immediately follow the header.
1734 offset = sizeof(mpack_builder_page_t);
1735 }
1736
1737 // now see if we can find another build.
1738 offset = mpack_builder_align_build(offset);
1739 if (offset + sizeof(mpack_build_t) > mpack_builder_page_size(writer, page)) {
1740 mpack_log("not enough room in this page for another build\n");
1741 mpack_builder_page_t* next_page = page->next;
1742 mpack_builder_free_page(writer, page);
1743 page = next_page;
1744 if (page == NULL) {
1745 mpack_log("no more pages\n");
1746 // there are no more pages. we're done.
1747 break;
1748 }
1749 offset = mpack_builder_align_build(sizeof(mpack_builder_page_t));
1750 }
1751 if (offset + sizeof(mpack_build_t) > page->bytes_used) {
1752 // there is no more data. we're done.
1753 mpack_log("no more data\n");
1754 mpack_builder_free_page(writer, page);
1755 break;
1756 }
1757
1758 // we've found another build. loop around!
1759 build = (mpack_build_t*)((char*)page + offset);
1760 offset += sizeof(mpack_build_t);
1761 mpack_log("found build %p\n", (void*)build);
1762 }
1763
1764 mpack_log("done resolve.\n");
1765
1766 // We can now restore the error handler and call it if an error occurred.
1767 writer->error_fn = error_fn;
1768 if (writer->error_fn && mpack_writer_error(writer) != mpack_ok)
1769 writer->error_fn(writer, writer->error);
1770}
1771
1772static void mpack_builder_complete(mpack_writer_t* writer, mpack_type_t type) {
1773 mpack_writer_track_pop_builder(writer, type);
1774 if (mpack_writer_error(writer) != mpack_ok)
1775 return;
1776
1777 mpack_builder_t* builder = &writer->builder;
1778 mpack_assert(builder->current_build != NULL, "no build in progress!");
1779 mpack_assert(builder->latest_build != NULL, "missing latest build!");
1780 mpack_assert(builder->current_build->type == type, "completing wrong type!");
1781 mpack_log("completing build %p\n", (void*)builder->current_build);
1782
1783 if (builder->current_build->key_needs_value) {
1784 mpack_break("an odd number of elements were written in a map!");
1785 mpack_writer_flag_error(writer, mpack_error_bug);
1786 return;
1787 }
1788
1789 if (builder->current_build->nested_compound_elements != 0) {
1790 mpack_break("there is a nested unfinished non-build map or array in this build.");
1791 mpack_writer_flag_error(writer, mpack_error_bug);
1792 return;
1793 }
1794
1795 // We need to apply whatever writes have been made to the current build
1796 // before popping it.
1797 mpack_builder_apply_writes(writer);
1798
1799 // For a nested build, we just switch the current build back to its parent.
1800 if (builder->current_build->parent != NULL) {
1801 mpack_log("setting current build to parent build %p. latest is still %p.\n",
1802 (void*)builder->current_build->parent, (void*)builder->latest_build);
1803 builder->current_build = builder->current_build->parent;
1804 mpack_builder_configure_buffer(writer);
1805 } else {
1806 // We're completing the final build.
1807 mpack_builder_resolve(writer);
1808 }
1809}
1810
1811void mpack_build_map(mpack_writer_t* writer) {
1812 mpack_builder_build(writer, mpack_type_map);
1813}
1814
1815void mpack_build_array(mpack_writer_t* writer) {
1816 mpack_builder_build(writer, mpack_type_array);
1817}
1818
1819void mpack_complete_map(mpack_writer_t* writer) {
1820 mpack_builder_complete(writer, mpack_type_map);
1821}
1822
1823void mpack_complete_array(mpack_writer_t* writer) {
1824 mpack_builder_complete(writer, mpack_type_array);
1825}
1826
1827#endif // MPACK_BUILDER
1828#endif // MPACK_WRITER
1829
1830MPACK_SILENCE_WARNINGS_END
1831