| 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 | |
| 26 | MPACK_SILENCE_WARNINGS_BEGIN |
| 27 | |
| 28 | #if MPACK_WRITER |
| 29 | |
| 30 | #if MPACK_BUILDER |
| 31 | static void mpack_builder_flush(mpack_writer_t* writer); |
| 32 | #endif |
| 33 | |
| 34 | #if MPACK_WRITE_TRACKING |
| 35 | static 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 | |
| 40 | void 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 | |
| 45 | void 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 | |
| 50 | void 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 | |
| 55 | void 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 | |
| 60 | void 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. |
| 67 | static 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 | |
| 95 | static 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 | |
| 124 | void 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 | |
| 139 | void 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 | |
| 147 | void 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 |
| 164 | typedef struct mpack_growable_writer_t { |
| 165 | char** target_data; |
| 166 | size_t* target_size; |
| 167 | } mpack_growable_writer_t; |
| 168 | |
| 169 | static 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 | |
| 176 | static 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 | |
| 239 | static 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 | |
| 276 | void 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 |
| 304 | static 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 | |
| 311 | static void mpack_file_writer_teardown(mpack_writer_t* writer) { |
| 312 | MPACK_FREE(writer->buffer); |
| 313 | writer->buffer = NULL; |
| 314 | writer->context = NULL; |
| 315 | } |
| 316 | |
| 317 | static 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 | |
| 329 | void 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 | |
| 350 | void 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 | |
| 363 | void 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 | |
| 373 | MPACK_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 | |
| 382 | void 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. |
| 414 | MPACK_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. |
| 457 | MPACK_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. |
| 520 | MPACK_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 | |
| 531 | mpack_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 | |
| 592 | void 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 | |
| 638 | MPACK_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 | |
| 644 | void mpack_write_nil(mpack_writer_t* writer) { |
| 645 | mpack_write_byte_element(writer, (char)0xc0); |
| 646 | } |
| 647 | |
| 648 | void mpack_write_bool(mpack_writer_t* writer, bool value) { |
| 649 | mpack_write_byte_element(writer, (char)(0xc2 | (value ? 1 : 0))); |
| 650 | } |
| 651 | |
| 652 | void mpack_write_true(mpack_writer_t* writer) { |
| 653 | mpack_write_byte_element(writer, (char)0xc3); |
| 654 | } |
| 655 | |
| 656 | void mpack_write_false(mpack_writer_t* writer) { |
| 657 | mpack_write_byte_element(writer, (char)0xc2); |
| 658 | } |
| 659 | |
| 660 | void 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 | |
| 669 | MPACK_STATIC_INLINE void mpack_encode_fixuint(char* p, uint8_t value) { |
| 670 | mpack_assert(value <= 127); |
| 671 | mpack_store_u8(p, value); |
| 672 | } |
| 673 | |
| 674 | MPACK_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 | |
| 680 | MPACK_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 | |
| 686 | MPACK_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 | |
| 692 | MPACK_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 | |
| 698 | MPACK_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 | |
| 704 | MPACK_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 | |
| 710 | MPACK_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 | |
| 716 | MPACK_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 | |
| 722 | MPACK_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 |
| 729 | MPACK_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 |
| 734 | MPACK_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 |
| 741 | MPACK_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 |
| 746 | MPACK_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 | |
| 752 | MPACK_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 | |
| 757 | MPACK_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 | |
| 763 | MPACK_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 | |
| 769 | MPACK_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 | |
| 774 | MPACK_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 | |
| 780 | MPACK_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 | |
| 786 | MPACK_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 | |
| 791 | MPACK_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 | |
| 797 | MPACK_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 | |
| 805 | MPACK_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 | |
| 811 | MPACK_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 | |
| 816 | MPACK_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 | |
| 822 | MPACK_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 |
| 829 | MPACK_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 | |
| 834 | MPACK_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 | |
| 839 | MPACK_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 | |
| 844 | MPACK_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 | |
| 849 | MPACK_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 | |
| 854 | MPACK_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 | |
| 861 | MPACK_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 | |
| 868 | MPACK_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 | |
| 875 | MPACK_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 | |
| 880 | MPACK_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 | |
| 887 | MPACK_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 | |
| 911 | void 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 | |
| 924 | void 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 | |
| 939 | void 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 | |
| 956 | void 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 | |
| 972 | void 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 | |
| 986 | void 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 | |
| 1008 | void 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 | |
| 1034 | void 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 |
| 1072 | void 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 |
| 1077 | void 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 |
| 1084 | void 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 |
| 1089 | void 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 |
| 1096 | void 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 | |
| 1123 | static 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 | |
| 1133 | static 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 | |
| 1143 | void 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 | |
| 1150 | void 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 | |
| 1157 | static 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 | |
| 1176 | static 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 | |
| 1196 | void 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 | |
| 1202 | void 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 |
| 1209 | void 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 | |
| 1248 | void 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 | |
| 1303 | void 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 |
| 1311 | void 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 | |
| 1319 | void 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 | |
| 1325 | void 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 | |
| 1333 | void 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 | |
| 1340 | void 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 | |
| 1349 | void 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 | |
| 1359 | void 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 | |
| 1406 | static 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 | |
| 1442 | static 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 | |
| 1453 | static 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 | |
| 1461 | static 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 | |
| 1472 | static 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 | |
| 1476 | static 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 | |
| 1492 | static 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. |
| 1512 | static 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 | |
| 1529 | static 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 | |
| 1536 | MPACK_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 | |
| 1570 | static 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 | |
| 1637 | MPACK_NOINLINE |
| 1638 | static 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 | |
| 1772 | static 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 | |
| 1811 | void mpack_build_map(mpack_writer_t* writer) { |
| 1812 | mpack_builder_build(writer, mpack_type_map); |
| 1813 | } |
| 1814 | |
| 1815 | void mpack_build_array(mpack_writer_t* writer) { |
| 1816 | mpack_builder_build(writer, mpack_type_array); |
| 1817 | } |
| 1818 | |
| 1819 | void mpack_complete_map(mpack_writer_t* writer) { |
| 1820 | mpack_builder_complete(writer, mpack_type_map); |
| 1821 | } |
| 1822 | |
| 1823 | void 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 | |
| 1830 | MPACK_SILENCE_WARNINGS_END |
| 1831 | |