builder.c (65233B)
1 /* 2 * Codegenerator for C, building FlatBuffers. 3 * 4 * There are several approaches, some light, some requiring a library, 5 * some with vectored I/O etc. 6 * 7 * Here we focus on a reasonable balance of light code and efficiency. 8 * 9 * Builder code is generated to a separate file that includes the 10 * generated read-only code. 11 * 12 * Mutable buffers are not supported in this version. 13 * 14 */ 15 16 #include <stdlib.h> 17 #include <string.h> 18 19 #include "flatcc_builder.h" 20 #include "flatcc_emitter.h" 21 22 /* 23 * `check` is designed to handle incorrect use errors that can be 24 * ignored in production of a tested product. 25 * 26 * `check_error` fails if condition is false and is designed to return an 27 * error code in production. 28 */ 29 30 #if FLATCC_BUILDER_ASSERT_ON_ERROR 31 #define check(cond, reason) FLATCC_BUILDER_ASSERT(cond, reason) 32 #else 33 #define check(cond, reason) ((void)0) 34 #endif 35 36 #if FLATCC_BUILDER_SKIP_CHECKS 37 #define check_error(cond, err, reason) ((void)0) 38 #else 39 #define check_error(cond, err, reason) if (!(cond)) { check(cond, reason); return err; } 40 #endif 41 42 /* `strnlen` not widely supported. */ 43 static inline size_t pstrnlen(const char *s, size_t max_len) 44 { 45 const char *end = memchr(s, 0, max_len); 46 return end ? (size_t)(end - s) : max_len; 47 } 48 #undef strnlen 49 #define strnlen pstrnlen 50 51 /* Padding can be up to 255 zeroes, and 1 zero string termination byte. 52 * When two paddings are combined at nested buffers, we need twice that. 53 * Visible to emitter so it can test for zero padding in iov. */ 54 const uint8_t flatcc_builder_padding_base[512] = { 0 }; 55 #define _pad flatcc_builder_padding_base 56 57 #define uoffset_t flatbuffers_uoffset_t 58 #define soffset_t flatbuffers_soffset_t 59 #define voffset_t flatbuffers_voffset_t 60 #define utype_t flatbuffers_utype_t 61 62 #define write_uoffset __flatbuffers_uoffset_write_to_pe 63 #define write_voffset __flatbuffers_voffset_write_to_pe 64 #define write_identifier __flatbuffers_uoffset_write_to_pe 65 #define write_utype __flatbuffers_utype_write_to_pe 66 67 #define field_size sizeof(uoffset_t) 68 #define max_offset_count FLATBUFFERS_COUNT_MAX(field_size) 69 #define union_size sizeof(flatcc_builder_union_ref_t) 70 #define max_union_count FLATBUFFERS_COUNT_MAX(union_size) 71 #define utype_size sizeof(utype_t) 72 #define max_utype_count FLATBUFFERS_COUNT_MAX(utype_size) 73 74 #define max_string_len FLATBUFFERS_COUNT_MAX(1) 75 #define identifier_size FLATBUFFERS_IDENTIFIER_SIZE 76 77 78 #define iovec_t flatcc_iovec_t 79 #define frame_size sizeof(__flatcc_builder_frame_t) 80 #define frame(x) (B->frame[0].x) 81 82 83 /* `align` must be a power of 2. */ 84 static inline uoffset_t alignup_uoffset(uoffset_t x, size_t align) 85 { 86 return (x + (uoffset_t)align - 1u) & ~((uoffset_t)align - 1u); 87 } 88 89 static inline size_t alignup_size(size_t x, size_t align) 90 { 91 return (x + align - 1u) & ~(align - 1u); 92 } 93 94 95 typedef struct vtable_descriptor vtable_descriptor_t; 96 struct vtable_descriptor { 97 /* Where the vtable is emitted. */ 98 flatcc_builder_ref_t vt_ref; 99 /* Which buffer it was emitted to. */ 100 uoffset_t nest_id; 101 /* Where the vtable is cached. */ 102 uoffset_t vb_start; 103 /* Hash table collision chain. */ 104 uoffset_t next; 105 }; 106 107 typedef struct flatcc_iov_state flatcc_iov_state_t; 108 struct flatcc_iov_state { 109 size_t len; 110 int count; 111 flatcc_iovec_t iov[FLATCC_IOV_COUNT_MAX]; 112 }; 113 114 #define iov_state_t flatcc_iov_state_t 115 116 /* This assumes `iov_state_t iov;` has been declared in scope */ 117 #define push_iov_cond(base, size, cond) if ((size) > 0 && (cond)) { iov.len += size;\ 118 iov.iov[iov.count].iov_base = (void *)(base); iov.iov[iov.count].iov_len = (size); ++iov.count; } 119 #define push_iov(base, size) push_iov_cond(base, size, 1) 120 #define init_iov() { iov.len = 0; iov.count = 0; } 121 122 123 int flatcc_builder_default_alloc(void *alloc_context, iovec_t *b, size_t request, int zero_fill, int hint) 124 { 125 void *p; 126 size_t n; 127 128 (void)alloc_context; 129 130 if (request == 0) { 131 if (b->iov_base) { 132 FLATCC_BUILDER_FREE(b->iov_base); 133 b->iov_base = 0; 134 b->iov_len = 0; 135 } 136 return 0; 137 } 138 switch (hint) { 139 case flatcc_builder_alloc_ds: 140 n = 256; 141 break; 142 case flatcc_builder_alloc_ht: 143 /* Should be exact size, or space size is just wasted. */ 144 n = request; 145 break; 146 case flatcc_builder_alloc_fs: 147 n = sizeof(__flatcc_builder_frame_t) * 8; 148 break; 149 case flatcc_builder_alloc_us: 150 n = 64; 151 break; 152 default: 153 /* 154 * We have many small structures - vs stack for tables with few 155 * elements, and few offset fields in patch log. No need to 156 * overallocate in case of busy small messages. 157 */ 158 n = 32; 159 break; 160 } 161 while (n < request) { 162 n *= 2; 163 } 164 if (request <= b->iov_len && b->iov_len / 2 >= n) { 165 /* Add hysteresis to shrink. */ 166 return 0; 167 } 168 if (!(p = FLATCC_BUILDER_REALLOC(b->iov_base, n))) { 169 return -1; 170 } 171 /* Realloc might also shrink. */ 172 if (zero_fill && b->iov_len < n) { 173 memset((uint8_t *)p + b->iov_len, 0, n - b->iov_len); 174 } 175 b->iov_base = p; 176 b->iov_len = n; 177 return 0; 178 } 179 180 #define T_ptr(base, pos) ((void *)((size_t)(base) + (size_t)(pos))) 181 #define ds_ptr(pos) (T_ptr(B->buffers[flatcc_builder_alloc_ds].iov_base, (pos))) 182 #define vs_ptr(pos) (T_ptr(B->buffers[flatcc_builder_alloc_vs].iov_base, (pos))) 183 #define pl_ptr(pos) (T_ptr(B->buffers[flatcc_builder_alloc_pl].iov_base, (pos))) 184 #define us_ptr(pos) (T_ptr(B->buffers[flatcc_builder_alloc_us].iov_base, (pos))) 185 #define vd_ptr(pos) (T_ptr(B->buffers[flatcc_builder_alloc_vd].iov_base, (pos))) 186 #define vb_ptr(pos) (T_ptr(B->buffers[flatcc_builder_alloc_vb].iov_base, (pos))) 187 #define vs_offset(ptr) ((uoffset_t)((size_t)(ptr) - (size_t)B->buffers[flatcc_builder_alloc_vs].iov_base)) 188 #define pl_offset(ptr) ((uoffset_t)((size_t)(ptr) - (size_t)B->buffers[flatcc_builder_alloc_pl].iov_base)) 189 #define us_offset(ptr) ((uoffset_t)((size_t)(ptr) - (size_t)B->buffers[flatcc_builder_alloc_us].iov_base)) 190 191 #define table_limit (FLATBUFFERS_VOFFSET_MAX - field_size + 1) 192 #define data_limit (FLATBUFFERS_UOFFSET_MAX - field_size + 1) 193 194 #define set_identifier(id) memcpy(&B->identifier, (id) ? (void *)(id) : (void *)_pad, identifier_size) 195 196 /* Must also return true when no buffer has been started. */ 197 #define is_top_buffer(B) (B->nest_id == 0) 198 199 /* 200 * Tables use a stack represention better suited for quickly adding 201 * fields to tables, but it must occasionally be refreshed following 202 * reallocation or reentry from child frame. 203 */ 204 static inline void refresh_ds(flatcc_builder_t *B, uoffset_t type_limit) 205 { 206 iovec_t *buf = B->buffers + flatcc_builder_alloc_ds; 207 208 B->ds = ds_ptr(B->ds_first); 209 B->ds_limit = (uoffset_t)buf->iov_len - B->ds_first; 210 /* 211 * So we don't allocate outside tables representation size, nor our 212 * current buffer size. 213 */ 214 if (B->ds_limit > type_limit) { 215 B->ds_limit = type_limit; 216 } 217 /* So exit frame can refresh fast. */ 218 frame(type_limit) = type_limit; 219 } 220 221 static int reserve_ds(flatcc_builder_t *B, size_t need, uoffset_t limit) 222 { 223 iovec_t *buf = B->buffers + flatcc_builder_alloc_ds; 224 225 if (B->alloc(B->alloc_context, buf, B->ds_first + need, 1, flatcc_builder_alloc_ds)) { 226 return -1; 227 } 228 refresh_ds(B, limit); 229 return 0; 230 } 231 232 /* 233 * Make sure there is always an extra zero termination on stack 234 * even if it isn't emitted such that string updates may count 235 * on zero termination being present always. 236 */ 237 static inline void *push_ds(flatcc_builder_t *B, uoffset_t size) 238 { 239 size_t offset; 240 241 offset = B->ds_offset; 242 if ((B->ds_offset += size) >= B->ds_limit) { 243 if (reserve_ds(B, B->ds_offset + 1, data_limit)) { 244 return 0; 245 } 246 } 247 return B->ds + offset; 248 } 249 250 static inline void unpush_ds(flatcc_builder_t *B, uoffset_t size) 251 { 252 B->ds_offset -= size; 253 memset(B->ds + B->ds_offset, 0, size); 254 } 255 256 static inline void *push_ds_copy(flatcc_builder_t *B, const void *data, uoffset_t size) 257 { 258 void *p; 259 260 if (!(p = push_ds(B, size))) { 261 return 0; 262 } 263 memcpy(p, data, size); 264 return p; 265 } 266 267 static inline void *push_ds_field(flatcc_builder_t *B, uoffset_t size, uint16_t align, voffset_t id) 268 { 269 uoffset_t offset; 270 271 /* 272 * We calculate table field alignment relative to first entry, not 273 * header field with vtable offset. 274 * 275 * Note: >= comparison handles special case where B->ds is not 276 * allocated yet and size is 0 so the return value would be mistaken 277 * for an error. 278 */ 279 offset = alignup_uoffset(B->ds_offset, align); 280 if ((B->ds_offset = offset + size) >= B->ds_limit) { 281 if (reserve_ds(B, B->ds_offset + 1, table_limit)) { 282 return 0; 283 } 284 } 285 B->vs[id] = (voffset_t)(offset + field_size); 286 if (id >= B->id_end) { 287 B->id_end = id + 1u; 288 } 289 return B->ds + offset; 290 } 291 292 static inline void *push_ds_offset_field(flatcc_builder_t *B, voffset_t id) 293 { 294 uoffset_t offset; 295 296 offset = alignup_uoffset(B->ds_offset, field_size); 297 if ((B->ds_offset = offset + field_size) > B->ds_limit) { 298 if (reserve_ds(B, B->ds_offset, table_limit)) { 299 return 0; 300 } 301 } 302 B->vs[id] = (voffset_t)(offset + field_size); 303 if (id >= B->id_end) { 304 B->id_end = id + 1u; 305 } 306 *B->pl++ = (flatbuffers_voffset_t)offset; 307 return B->ds + offset; 308 } 309 310 static inline void *reserve_buffer(flatcc_builder_t *B, int alloc_type, size_t used, size_t need, int zero_init) 311 { 312 iovec_t *buf = B->buffers + alloc_type; 313 314 if (used + need > buf->iov_len) { 315 if (B->alloc(B->alloc_context, buf, used + need, zero_init, alloc_type)) { 316 check(0, "memory allocation failed"); 317 return 0; 318 } 319 } 320 return (void *)((size_t)buf->iov_base + used); 321 } 322 323 static inline int reserve_fields(flatcc_builder_t *B, int count) 324 { 325 size_t used, need; 326 327 /* Provide faster stack operations for common table operations. */ 328 used = frame(container.table.vs_end) + frame(container.table.id_end) * sizeof(voffset_t); 329 need = (size_t)(count + 2) * sizeof(voffset_t); 330 if (!(B->vs = reserve_buffer(B, flatcc_builder_alloc_vs, used, need, 1))) { 331 return -1; 332 } 333 /* Move past header for convenience. */ 334 B->vs += 2; 335 used = frame(container.table.pl_end); 336 /* Add one to handle special case of first table being empty. */ 337 need = (size_t)count * sizeof(*(B->pl)) + 1; 338 if (!(B->pl = reserve_buffer(B, flatcc_builder_alloc_pl, used, need, 0))) { 339 return -1; 340 } 341 return 0; 342 } 343 344 static int alloc_ht(flatcc_builder_t *B) 345 { 346 iovec_t *buf = B->buffers + flatcc_builder_alloc_ht; 347 348 size_t size, k; 349 /* Allocate null entry so we can check for return errors. */ 350 FLATCC_ASSERT(B->vd_end == 0); 351 if (!reserve_buffer(B, flatcc_builder_alloc_vd, B->vd_end, sizeof(vtable_descriptor_t), 0)) { 352 return -1; 353 } 354 B->vd_end = sizeof(vtable_descriptor_t); 355 size = field_size * FLATCC_BUILDER_MIN_HASH_COUNT; 356 if (B->alloc(B->alloc_context, buf, size, 1, flatcc_builder_alloc_ht)) { 357 return -1; 358 } 359 while (size * 2 <= buf->iov_len) { 360 size *= 2; 361 } 362 size /= field_size; 363 for (k = 0; (((size_t)1) << k) < size; ++k) { 364 } 365 B->ht_width = k; 366 return 0; 367 } 368 369 static inline uoffset_t *lookup_ht(flatcc_builder_t *B, uint32_t hash) 370 { 371 uoffset_t *T; 372 373 if (B->ht_width == 0) { 374 if (alloc_ht(B)) { 375 return 0; 376 } 377 } 378 T = B->buffers[flatcc_builder_alloc_ht].iov_base; 379 380 return &T[FLATCC_BUILDER_BUCKET_VT_HASH(hash, B->ht_width)]; 381 } 382 383 void flatcc_builder_flush_vtable_cache(flatcc_builder_t *B) 384 { 385 iovec_t *buf = B->buffers + flatcc_builder_alloc_ht; 386 387 if (B->ht_width == 0) { 388 return; 389 } 390 memset(buf->iov_base, 0, buf->iov_len); 391 /* Reserve the null entry. */ 392 B->vd_end = sizeof(vtable_descriptor_t); 393 B->vb_end = 0; 394 } 395 396 int flatcc_builder_custom_init(flatcc_builder_t *B, 397 flatcc_builder_emit_fun *emit, void *emit_context, 398 flatcc_builder_alloc_fun *alloc, void *alloc_context) 399 { 400 /* 401 * Do not allocate anything here. Only the required buffers will be 402 * allocated. For simple struct buffers, no allocation is required 403 * at all. 404 */ 405 memset(B, 0, sizeof(*B)); 406 407 if (emit == 0) { 408 B->is_default_emitter = 1; 409 emit = flatcc_emitter; 410 emit_context = &B->default_emit_context; 411 } 412 if (alloc == 0) { 413 alloc = flatcc_builder_default_alloc; 414 } 415 B->alloc_context = alloc_context; 416 B->alloc = alloc; 417 B->emit_context = emit_context; 418 B->emit = emit; 419 return 0; 420 } 421 422 int flatcc_builder_init(flatcc_builder_t *B) 423 { 424 return flatcc_builder_custom_init(B, 0, 0, 0, 0); 425 } 426 427 int flatcc_builder_custom_reset(flatcc_builder_t *B, int set_defaults, int reduce_buffers) 428 { 429 iovec_t *buf; 430 int i; 431 432 for (i = 0; i < FLATCC_BUILDER_ALLOC_BUFFER_COUNT; ++i) { 433 buf = B->buffers + i; 434 if (buf->iov_base) { 435 /* Don't try to reduce the hash table. */ 436 if (i != flatcc_builder_alloc_ht && 437 reduce_buffers && B->alloc(B->alloc_context, buf, 1, 1, i)) { 438 return -1; 439 } 440 memset(buf->iov_base, 0, buf->iov_len); 441 } else { 442 FLATCC_ASSERT(buf->iov_len == 0); 443 } 444 } 445 B->vb_end = 0; 446 if (B->vd_end > 0) { 447 /* Reset past null entry. */ 448 B->vd_end = sizeof(vtable_descriptor_t); 449 } 450 B->min_align = 0; 451 B->emit_start = 0; 452 B->emit_end = 0; 453 B->level = 0; 454 B->limit_level = 0; 455 B->ds_offset = 0; 456 B->ds_limit = 0; 457 B->nest_count = 0; 458 B->nest_id = 0; 459 /* Needed for correct offset calculation. */ 460 B->ds = B->buffers[flatcc_builder_alloc_ds].iov_base; 461 B->pl = B->buffers[flatcc_builder_alloc_pl].iov_base; 462 B->vs = B->buffers[flatcc_builder_alloc_vs].iov_base; 463 B->frame = 0; 464 if (set_defaults) { 465 B->vb_flush_limit = 0; 466 B->max_level = 0; 467 B->disable_vt_clustering = 0; 468 } 469 if (B->is_default_emitter) { 470 flatcc_emitter_reset(&B->default_emit_context); 471 } 472 if (B->refmap) { 473 flatcc_refmap_reset(B->refmap); 474 } 475 return 0; 476 } 477 478 int flatcc_builder_reset(flatcc_builder_t *B) 479 { 480 return flatcc_builder_custom_reset(B, 0, 0); 481 } 482 483 void flatcc_builder_clear(flatcc_builder_t *B) 484 { 485 iovec_t *buf; 486 int i; 487 488 for (i = 0; i < FLATCC_BUILDER_ALLOC_BUFFER_COUNT; ++i) { 489 buf = B->buffers + i; 490 B->alloc(B->alloc_context, buf, 0, 0, i); 491 } 492 if (B->is_default_emitter) { 493 flatcc_emitter_clear(&B->default_emit_context); 494 } 495 if (B->refmap) { 496 flatcc_refmap_clear(B->refmap); 497 } 498 memset(B, 0, sizeof(*B)); 499 } 500 501 static inline void set_min_align(flatcc_builder_t *B, uint16_t align) 502 { 503 if (B->min_align < align) { 504 B->min_align = align; 505 } 506 } 507 508 /* 509 * It's a max, but the minimum viable alignment is the largest observed 510 * alignment requirement, but no larger. 511 */ 512 static inline void get_min_align(uint16_t *align, uint16_t b) 513 { 514 if (*align < b) { 515 *align = b; 516 } 517 } 518 519 void *flatcc_builder_enter_user_frame_ptr(flatcc_builder_t *B, size_t size) 520 { 521 size_t *frame; 522 523 size = alignup_size(size, sizeof(size_t)) + sizeof(size_t); 524 525 if (!(frame = reserve_buffer(B, flatcc_builder_alloc_us, B->user_frame_end, size, 0))) { 526 return 0; 527 } 528 memset(frame, 0, size); 529 *frame++ = B->user_frame_offset; 530 B->user_frame_offset = B->user_frame_end + sizeof(size_t); 531 B->user_frame_end += size; 532 return frame; 533 } 534 535 size_t flatcc_builder_enter_user_frame(flatcc_builder_t *B, size_t size) 536 { 537 size_t *frame; 538 539 size = alignup_size(size, sizeof(size_t)) + sizeof(size_t); 540 541 if (!(frame = reserve_buffer(B, flatcc_builder_alloc_us, B->user_frame_end, size, 0))) { 542 return 0; 543 } 544 memset(frame, 0, size); 545 *frame++ = B->user_frame_offset; 546 B->user_frame_offset = B->user_frame_end + sizeof(size_t); 547 B->user_frame_end += size; 548 return B->user_frame_offset; 549 } 550 551 552 size_t flatcc_builder_exit_user_frame(flatcc_builder_t *B) 553 { 554 size_t *hdr; 555 556 FLATCC_ASSERT(B->user_frame_offset > 0); 557 558 hdr = us_ptr(B->user_frame_offset); 559 B->user_frame_end = B->user_frame_offset - sizeof(size_t); 560 return B->user_frame_offset = hdr[-1]; 561 } 562 563 size_t flatcc_builder_exit_user_frame_at(flatcc_builder_t *B, size_t handle) 564 { 565 FLATCC_ASSERT(B->user_frame_offset >= handle); 566 567 B->user_frame_offset = handle; 568 return flatcc_builder_exit_user_frame(B); 569 } 570 571 size_t flatcc_builder_get_current_user_frame(flatcc_builder_t *B) 572 { 573 return B->user_frame_offset; 574 } 575 576 void *flatcc_builder_get_user_frame_ptr(flatcc_builder_t *B, size_t handle) 577 { 578 return us_ptr(handle); 579 } 580 581 static int enter_frame(flatcc_builder_t *B, uint16_t align) 582 { 583 if (++B->level > B->limit_level) { 584 if (B->max_level > 0 && B->level > B->max_level) { 585 return -1; 586 } 587 if (!(B->frame = reserve_buffer(B, flatcc_builder_alloc_fs, 588 (size_t)(B->level - 1) * frame_size, frame_size, 0))) { 589 return -1; 590 } 591 B->limit_level = (int)(B->buffers[flatcc_builder_alloc_fs].iov_len / frame_size); 592 if (B->max_level > 0 && B->max_level < B->limit_level) { 593 B->limit_level = B->max_level; 594 } 595 } else { 596 ++B->frame; 597 } 598 frame(ds_offset) = B->ds_offset; 599 frame(align) = B->align; 600 B->align = align; 601 /* Note: do not assume padding before first has been allocated! */ 602 frame(ds_first) = B->ds_first; 603 frame(type_limit) = data_limit; 604 B->ds_first = alignup_uoffset(B->ds_first + B->ds_offset, 8); 605 B->ds_offset = 0; 606 return 0; 607 } 608 609 static inline void exit_frame(flatcc_builder_t *B) 610 { 611 memset(B->ds, 0, B->ds_offset); 612 B->ds_offset = frame(ds_offset); 613 B->ds_first = frame(ds_first); 614 refresh_ds(B, frame(type_limit)); 615 616 /* 617 * Restore local alignment: e.g. a table should not change alignment 618 * because a child table was just created elsewhere in the buffer, 619 * but the overall alignment (min align), should be aware of it. 620 * Each buffer has its own min align that then migrates up without 621 * being affected by sibling or child buffers. 622 */ 623 set_min_align(B, B->align); 624 B->align = frame(align); 625 626 --B->frame; 627 --B->level; 628 } 629 630 static inline uoffset_t front_pad(flatcc_builder_t *B, uoffset_t size, uint16_t align) 631 { 632 return (uoffset_t)(B->emit_start - (flatcc_builder_ref_t)size) & (align - 1u); 633 } 634 635 static inline uoffset_t back_pad(flatcc_builder_t *B, uint16_t align) 636 { 637 return (uoffset_t)(B->emit_end) & (align - 1u); 638 } 639 640 static inline flatcc_builder_ref_t emit_front(flatcc_builder_t *B, iov_state_t *iov) 641 { 642 flatcc_builder_ref_t ref; 643 644 /* 645 * We might have overflow when including headers, but without 646 * headers we should have checks to prevent overflow in the 647 * uoffset_t range, hence we subtract 16 to be safe. With that 648 * guarantee we can also make a safe check on the soffset_t range. 649 * 650 * We only allow buffers half the theoritical size of 651 * FLATBUFFERS_UOFFSET_MAX so we can safely use signed references. 652 * 653 * NOTE: vtables vt_offset field is signed, and the check in create 654 * table only ensures the signed limit. The check would fail if the 655 * total buffer size could grow beyond UOFFSET_MAX, and we prevent 656 * that by limiting the lower end to SOFFSET_MIN, and the upper end 657 * at emit_back to SOFFSET_MAX. 658 */ 659 ref = B->emit_start - (flatcc_builder_ref_t)iov->len; 660 if ((iov->len > 16 && iov->len - 16 > FLATBUFFERS_UOFFSET_MAX) || ref >= B->emit_start) { 661 check(0, "buffer too large to represent"); 662 return 0; 663 } 664 if (B->emit(B->emit_context, iov->iov, iov->count, ref, iov->len)) { 665 check(0, "emitter rejected buffer content"); 666 return 0; 667 } 668 return B->emit_start = ref; 669 } 670 671 static inline flatcc_builder_ref_t emit_back(flatcc_builder_t *B, iov_state_t *iov) 672 { 673 flatcc_builder_ref_t ref; 674 675 ref = B->emit_end; 676 B->emit_end = ref + (flatcc_builder_ref_t)iov->len; 677 /* 678 * Similar to emit_front check, but since we only emit vtables and 679 * padding at the back, we are not concerned with iov->len overflow, 680 * only total buffer overflow. 681 * 682 * With this check, vtable soffset references at table header can 683 * still overflow in extreme cases, so this must be checked 684 * separately. 685 */ 686 if (B->emit_end < ref) { 687 check(0, "buffer too large to represent"); 688 return 0; 689 } 690 if (B->emit(B->emit_context, iov->iov, iov->count, ref, iov->len)) { 691 check(0, "emitter rejected buffer content"); 692 return 0; 693 } 694 /* 695 * Back references always return ref + 1 because ref == 0 is valid and 696 * should not be mistaken for error. vtables understand this. 697 */ 698 return ref + 1; 699 } 700 701 /* If nested we cannot pad the end of the buffer without moving the entire buffer, so we don't. */ 702 static int align_buffer_end(flatcc_builder_t *B, uint16_t *align, uint16_t block_align, int is_nested) 703 { 704 size_t end_pad; 705 iov_state_t iov; 706 707 block_align = block_align ? block_align : B->block_align ? B->block_align : 1; 708 get_min_align(align, field_size); 709 get_min_align(align, block_align); 710 /* Pad end of buffer to multiple. */ 711 if (!is_nested) { 712 end_pad = back_pad(B, *align); 713 if (end_pad) { 714 init_iov(); 715 push_iov(_pad, end_pad); 716 if (0 == emit_back(B, &iov)) { 717 check(0, "emitter rejected buffer content"); 718 return -1; 719 } 720 } 721 } 722 return 0; 723 } 724 725 flatcc_builder_ref_t flatcc_builder_embed_buffer(flatcc_builder_t *B, 726 uint16_t block_align, 727 const void *data, size_t size, uint16_t align, flatcc_builder_buffer_flags_t flags) 728 { 729 uoffset_t size_field, pad; 730 iov_state_t iov; 731 int with_size = (flags & flatcc_builder_with_size) != 0; 732 733 if (align_buffer_end(B, &align, block_align, !is_top_buffer(B))) { 734 return 0; 735 } 736 pad = front_pad(B, (uoffset_t)(size + (with_size ? field_size : 0)), align); 737 write_uoffset(&size_field, (uoffset_t)size + pad); 738 init_iov(); 739 /* Add ubyte vector size header if nested buffer. */ 740 push_iov_cond(&size_field, field_size, !is_top_buffer(B)); 741 push_iov(data, size); 742 push_iov(_pad, pad); 743 return emit_front(B, &iov); 744 } 745 746 flatcc_builder_ref_t flatcc_builder_create_buffer(flatcc_builder_t *B, 747 const char identifier[identifier_size], uint16_t block_align, 748 flatcc_builder_ref_t object_ref, uint16_t align, flatcc_builder_buffer_flags_t flags) 749 { 750 flatcc_builder_ref_t buffer_ref; 751 uoffset_t header_pad, id_size = 0; 752 uoffset_t object_offset, buffer_size, buffer_base; 753 iov_state_t iov; 754 flatcc_builder_identifier_t id_out = 0; 755 int is_nested = (flags & flatcc_builder_is_nested) != 0; 756 int with_size = (flags & flatcc_builder_with_size) != 0; 757 758 if (align_buffer_end(B, &align, block_align, is_nested)) { 759 return 0; 760 } 761 set_min_align(B, align); 762 if (identifier) { 763 FLATCC_ASSERT(sizeof(flatcc_builder_identifier_t) == identifier_size); 764 FLATCC_ASSERT(sizeof(flatcc_builder_identifier_t) == field_size); 765 memcpy(&id_out, identifier, identifier_size); 766 id_out = __flatbuffers_thash_read_from_le(&id_out); 767 write_identifier(&id_out, id_out); 768 } 769 id_size = id_out ? identifier_size : 0; 770 header_pad = front_pad(B, field_size + id_size + (uoffset_t)(with_size ? field_size : 0), align); 771 init_iov(); 772 /* ubyte vectors size field wrapping nested buffer. */ 773 push_iov_cond(&buffer_size, field_size, is_nested || with_size); 774 push_iov(&object_offset, field_size); 775 /* Identifiers are not always present in buffer. */ 776 push_iov(&id_out, id_size); 777 push_iov(_pad, header_pad); 778 buffer_base = (uoffset_t)B->emit_start - (uoffset_t)iov.len + (uoffset_t)((is_nested || with_size) ? field_size : 0); 779 if (is_nested) { 780 write_uoffset(&buffer_size, (uoffset_t)B->buffer_mark - buffer_base); 781 } else { 782 /* Also include clustered vtables. */ 783 write_uoffset(&buffer_size, (uoffset_t)B->emit_end - buffer_base); 784 } 785 write_uoffset(&object_offset, (uoffset_t)object_ref - buffer_base); 786 if (0 == (buffer_ref = emit_front(B, &iov))) { 787 check(0, "emitter rejected buffer content"); 788 return 0; 789 } 790 return buffer_ref; 791 } 792 793 flatcc_builder_ref_t flatcc_builder_create_struct(flatcc_builder_t *B, const void *data, size_t size, uint16_t align) 794 { 795 size_t pad; 796 iov_state_t iov; 797 798 check(align >= 1, "align cannot be 0"); 799 set_min_align(B, align); 800 pad = front_pad(B, (uoffset_t)size, align); 801 init_iov(); 802 push_iov(data, size); 803 /* 804 * Normally structs will already be a multiple of their alignment, 805 * so this padding will not likely be emitted. 806 */ 807 push_iov(_pad, pad); 808 return emit_front(B, &iov); 809 } 810 811 int flatcc_builder_start_buffer(flatcc_builder_t *B, 812 const char identifier[identifier_size], uint16_t block_align, flatcc_builder_buffer_flags_t flags) 813 { 814 /* 815 * This saves the parent `min_align` in the align field since we 816 * shouldn't use that for the current buffer. `exit_frame` 817 * automatically aggregates align up, so it is updated when the 818 * buffer frame exits. 819 */ 820 if (enter_frame(B, B->min_align)) { 821 return -1; 822 } 823 /* B->align now has parent min_align, and child frames will save it. */ 824 /* Since we allow objects to be created before the buffer at top level, 825 we need to respect min_align in that case. */ 826 if (!is_top_buffer(B) || B->min_align == 0) { 827 B->min_align = 1; 828 } 829 /* Save the parent block align, and set proper defaults for this buffer. */ 830 frame(container.buffer.block_align) = B->block_align; 831 B->block_align = block_align; 832 frame(container.buffer.flags = B->buffer_flags); 833 B->buffer_flags = (uint16_t)flags; 834 frame(container.buffer.mark) = B->buffer_mark; 835 frame(container.buffer.nest_id) = B->nest_id; 836 /* 837 * End of buffer when nested. Not defined for top-level because we 838 * here (on only here) permit strings etc. to be created before buffer start and 839 * because top-level buffer vtables can be clustered. 840 */ 841 B->buffer_mark = B->emit_start; 842 /* Must be 0 before and after entering top-level buffer, and unique otherwise. */ 843 B->nest_id = B->nest_count++; 844 frame(container.buffer.identifier) = B->identifier; 845 set_identifier(identifier); 846 frame(type) = flatcc_builder_buffer; 847 return 0; 848 } 849 850 flatcc_builder_ref_t flatcc_builder_end_buffer(flatcc_builder_t *B, flatcc_builder_ref_t root) 851 { 852 flatcc_builder_ref_t buffer_ref; 853 flatcc_builder_buffer_flags_t flags; 854 855 flags = (flatcc_builder_buffer_flags_t)B->buffer_flags & flatcc_builder_with_size; 856 flags |= is_top_buffer(B) ? 0 : flatcc_builder_is_nested; 857 check(frame(type) == flatcc_builder_buffer, "expected buffer frame"); 858 set_min_align(B, B->block_align); 859 if (0 == (buffer_ref = flatcc_builder_create_buffer(B, (void *)&B->identifier, 860 B->block_align, root, B->min_align, flags))) { 861 return 0; 862 } 863 B->buffer_mark = frame(container.buffer.mark); 864 B->nest_id = frame(container.buffer.nest_id); 865 B->identifier = frame(container.buffer.identifier); 866 B->buffer_flags = frame(container.buffer.flags); 867 B->block_align = frame(container.buffer.block_align); 868 869 exit_frame(B); 870 return buffer_ref; 871 } 872 873 void *flatcc_builder_start_struct(flatcc_builder_t *B, size_t size, uint16_t align) 874 { 875 /* Allocate space for the struct on the ds stack. */ 876 if (enter_frame(B, align)) { 877 return 0; 878 } 879 frame(type) = flatcc_builder_struct; 880 refresh_ds(B, data_limit); 881 return push_ds(B, (uoffset_t)size); 882 } 883 884 void *flatcc_builder_struct_edit(flatcc_builder_t *B) 885 { 886 return B->ds; 887 } 888 889 flatcc_builder_ref_t flatcc_builder_end_struct(flatcc_builder_t *B) 890 { 891 flatcc_builder_ref_t object_ref; 892 893 check(frame(type) == flatcc_builder_struct, "expected struct frame"); 894 if (0 == (object_ref = flatcc_builder_create_struct(B, B->ds, B->ds_offset, B->align))) { 895 return 0; 896 } 897 exit_frame(B); 898 return object_ref; 899 } 900 901 static inline int vector_count_add(flatcc_builder_t *B, uoffset_t count, uoffset_t max_count) 902 { 903 uoffset_t n, n1; 904 n = frame(container.vector.count); 905 n1 = n + count; 906 /* 907 * This prevents elem_size * count from overflowing iff max_vector 908 * has been set sensible. Without this check we might allocate to 909 * little on the ds stack and return a buffer the user thinks is 910 * much larger which of course is bad even though the buffer eventually 911 * would fail anyway. 912 */ 913 check_error(n <= n1 && n1 <= max_count, -1, "vector too large to represent"); 914 frame(container.vector.count) = n1; 915 return 0; 916 } 917 918 void *flatcc_builder_extend_vector(flatcc_builder_t *B, size_t count) 919 { 920 if (vector_count_add(B, (uoffset_t)count, frame(container.vector.max_count))) { 921 return 0; 922 } 923 return push_ds(B, frame(container.vector.elem_size) * (uoffset_t)count); 924 } 925 926 void *flatcc_builder_vector_push(flatcc_builder_t *B, const void *data) 927 { 928 check(frame(type) == flatcc_builder_vector, "expected vector frame"); 929 check_error(frame(container.vector.count) <= frame(container.vector.max_count), 0, "vector max count exceeded"); 930 frame(container.vector.count) += 1; 931 return push_ds_copy(B, data, frame(container.vector.elem_size)); 932 } 933 934 void *flatcc_builder_append_vector(flatcc_builder_t *B, const void *data, size_t count) 935 { 936 check(frame(type) == flatcc_builder_vector, "expected vector frame"); 937 if (vector_count_add(B, (uoffset_t)count, frame(container.vector.max_count))) { 938 return 0; 939 } 940 return push_ds_copy(B, data, frame(container.vector.elem_size) * (uoffset_t)count); 941 } 942 943 flatcc_builder_ref_t *flatcc_builder_extend_offset_vector(flatcc_builder_t *B, size_t count) 944 { 945 if (vector_count_add(B, (uoffset_t)count, max_offset_count)) { 946 return 0; 947 } 948 return push_ds(B, (uoffset_t)(field_size * count)); 949 } 950 951 flatcc_builder_ref_t *flatcc_builder_offset_vector_push(flatcc_builder_t *B, flatcc_builder_ref_t ref) 952 { 953 flatcc_builder_ref_t *p; 954 955 check(frame(type) == flatcc_builder_offset_vector, "expected offset vector frame"); 956 if (frame(container.vector.count) == max_offset_count) { 957 return 0; 958 } 959 frame(container.vector.count) += 1; 960 if (0 == (p = push_ds(B, field_size))) { 961 return 0; 962 } 963 *p = ref; 964 return p; 965 } 966 967 flatcc_builder_ref_t *flatcc_builder_append_offset_vector(flatcc_builder_t *B, const flatcc_builder_ref_t *refs, size_t count) 968 { 969 check(frame(type) == flatcc_builder_offset_vector, "expected offset vector frame"); 970 if (vector_count_add(B, (uoffset_t)count, max_offset_count)) { 971 return 0; 972 } 973 return push_ds_copy(B, refs, (uoffset_t)(field_size * count)); 974 } 975 976 char *flatcc_builder_extend_string(flatcc_builder_t *B, size_t len) 977 { 978 check(frame(type) == flatcc_builder_string, "expected string frame"); 979 if (vector_count_add(B, (uoffset_t)len, max_string_len)) { 980 return 0; 981 } 982 return push_ds(B, (uoffset_t)len); 983 } 984 985 char *flatcc_builder_append_string(flatcc_builder_t *B, const char *s, size_t len) 986 { 987 check(frame(type) == flatcc_builder_string, "expected string frame"); 988 if (vector_count_add(B, (uoffset_t)len, max_string_len)) { 989 return 0; 990 } 991 return push_ds_copy(B, s, (uoffset_t)len); 992 } 993 994 char *flatcc_builder_append_string_str(flatcc_builder_t *B, const char *s) 995 { 996 return flatcc_builder_append_string(B, s, strlen(s)); 997 } 998 999 char *flatcc_builder_append_string_strn(flatcc_builder_t *B, const char *s, size_t max_len) 1000 { 1001 return flatcc_builder_append_string(B, s, strnlen(s, max_len)); 1002 } 1003 1004 int flatcc_builder_truncate_vector(flatcc_builder_t *B, size_t count) 1005 { 1006 check(frame(type) == flatcc_builder_vector, "expected vector frame"); 1007 check_error(frame(container.vector.count) >= count, -1, "cannot truncate vector past empty"); 1008 frame(container.vector.count) -= (uoffset_t)count; 1009 unpush_ds(B, frame(container.vector.elem_size) * (uoffset_t)count); 1010 return 0; 1011 } 1012 1013 int flatcc_builder_truncate_offset_vector(flatcc_builder_t *B, size_t count) 1014 { 1015 check(frame(type) == flatcc_builder_offset_vector, "expected offset vector frame"); 1016 check_error(frame(container.vector.count) >= (uoffset_t)count, -1, "cannot truncate vector past empty"); 1017 frame(container.vector.count) -= (uoffset_t)count; 1018 unpush_ds(B, frame(container.vector.elem_size) * (uoffset_t)count); 1019 return 0; 1020 } 1021 1022 int flatcc_builder_truncate_string(flatcc_builder_t *B, size_t len) 1023 { 1024 check(frame(type) == flatcc_builder_string, "expected string frame"); 1025 check_error(frame(container.vector.count) >= len, -1, "cannot truncate string past empty"); 1026 frame(container.vector.count) -= (uoffset_t)len; 1027 unpush_ds(B, (uoffset_t)len); 1028 return 0; 1029 } 1030 1031 int flatcc_builder_start_vector(flatcc_builder_t *B, size_t elem_size, uint16_t align, size_t max_count) 1032 { 1033 get_min_align(&align, field_size); 1034 if (enter_frame(B, align)) { 1035 return -1; 1036 } 1037 frame(container.vector.elem_size) = (uoffset_t)elem_size; 1038 frame(container.vector.count) = 0; 1039 frame(container.vector.max_count) = (uoffset_t)max_count; 1040 frame(type) = flatcc_builder_vector; 1041 refresh_ds(B, data_limit); 1042 return 0; 1043 } 1044 1045 int flatcc_builder_start_offset_vector(flatcc_builder_t *B) 1046 { 1047 if (enter_frame(B, field_size)) { 1048 return -1; 1049 } 1050 frame(container.vector.elem_size) = field_size; 1051 frame(container.vector.count) = 0; 1052 frame(type) = flatcc_builder_offset_vector; 1053 refresh_ds(B, data_limit); 1054 return 0; 1055 } 1056 1057 flatcc_builder_ref_t flatcc_builder_create_offset_vector(flatcc_builder_t *B, 1058 const flatcc_builder_ref_t *vec, size_t count) 1059 { 1060 flatcc_builder_ref_t *_vec; 1061 1062 if (flatcc_builder_start_offset_vector(B)) { 1063 return 0; 1064 } 1065 if (!(_vec = flatcc_builder_extend_offset_vector(B, count))) { 1066 return 0; 1067 } 1068 memcpy(_vec, vec, count * field_size); 1069 return flatcc_builder_end_offset_vector(B); 1070 } 1071 1072 int flatcc_builder_start_string(flatcc_builder_t *B) 1073 { 1074 if (enter_frame(B, 1)) { 1075 return -1; 1076 } 1077 frame(container.vector.elem_size) = 1; 1078 frame(container.vector.count) = 0; 1079 frame(type) = flatcc_builder_string; 1080 refresh_ds(B, data_limit); 1081 return 0; 1082 } 1083 1084 int flatcc_builder_reserve_table(flatcc_builder_t *B, int count) 1085 { 1086 check(count >= 0, "cannot reserve negative count"); 1087 return reserve_fields(B, count); 1088 } 1089 1090 int flatcc_builder_start_table(flatcc_builder_t *B, int count) 1091 { 1092 if (enter_frame(B, field_size)) { 1093 return -1; 1094 } 1095 frame(container.table.vs_end) = vs_offset(B->vs); 1096 frame(container.table.pl_end) = pl_offset(B->pl); 1097 frame(container.table.vt_hash) = B->vt_hash; 1098 frame(container.table.id_end) = B->id_end; 1099 B->vt_hash = 0; 1100 FLATCC_BUILDER_INIT_VT_HASH(B->vt_hash); 1101 B->id_end = 0; 1102 frame(type) = flatcc_builder_table; 1103 if (reserve_fields(B, count)) { 1104 return -1; 1105 } 1106 refresh_ds(B, table_limit); 1107 return 0; 1108 } 1109 1110 flatcc_builder_vt_ref_t flatcc_builder_create_vtable(flatcc_builder_t *B, 1111 const voffset_t *vt, voffset_t vt_size) 1112 { 1113 flatcc_builder_vt_ref_t vt_ref; 1114 iov_state_t iov; 1115 voffset_t *vt_; 1116 size_t i; 1117 1118 /* 1119 * Only top-level buffer can cluster vtables because only it can 1120 * extend beyond the end. 1121 * 1122 * We write the vtable after the referencing table to maintain 1123 * the construction invariant that any offset reference has 1124 * valid emitted data at a higher address, and also that any 1125 * issued negative emit address represents an offset reference 1126 * to some flatbuffer object or vector (or possibly a root 1127 * struct). 1128 * 1129 * The vt_ref is stored as the reference + 1 to avoid having 0 as a 1130 * valid reference (which usally means error). It also idententifies 1131 * vtable references as the only uneven references, and the only 1132 * references that can be used multiple times in the same buffer. 1133 * 1134 * We do the vtable conversion here so cached vtables can be built 1135 * hashed and compared more efficiently, and so end users with 1136 * direct vtable construction don't have to worry about endianness. 1137 * This also ensures the hash function works the same wrt. 1138 * collision frequency. 1139 */ 1140 1141 if (!flatbuffers_is_native_pe()) { 1142 /* Make space in vtable cache for temporary endian conversion. */ 1143 if (!(vt_ = reserve_buffer(B, flatcc_builder_alloc_vb, B->vb_end, vt_size, 0))) { 1144 return 0; 1145 } 1146 for (i = 0; i < vt_size / sizeof(voffset_t); ++i) { 1147 write_voffset(&vt_[i], vt[i]); 1148 } 1149 vt = vt_; 1150 /* We don't need to free the reservation since we don't advance any base pointer. */ 1151 } 1152 1153 init_iov(); 1154 push_iov(vt, vt_size); 1155 if (is_top_buffer(B) && !B->disable_vt_clustering) { 1156 /* Note that `emit_back` already returns ref + 1 as we require for vtables. */ 1157 if (0 == (vt_ref = emit_back(B, &iov))) { 1158 return 0; 1159 } 1160 } else { 1161 if (0 == (vt_ref = emit_front(B, &iov))) { 1162 return 0; 1163 } 1164 /* 1165 * We don't have a valid 0 ref here, but to be consistent with 1166 * clustered vtables we offset by one. This cannot be zero 1167 * either. 1168 */ 1169 vt_ref += 1; 1170 } 1171 return vt_ref; 1172 } 1173 1174 flatcc_builder_vt_ref_t flatcc_builder_create_cached_vtable(flatcc_builder_t *B, 1175 const voffset_t *vt, voffset_t vt_size, uint32_t vt_hash) 1176 { 1177 vtable_descriptor_t *vd, *vd2; 1178 uoffset_t *pvd, *pvd_head; 1179 uoffset_t next; 1180 voffset_t *vt_; 1181 1182 /* This just gets the hash table slot, we still have to inspect it. */ 1183 if (!(pvd_head = lookup_ht(B, vt_hash))) { 1184 return 0; 1185 } 1186 pvd = pvd_head; 1187 next = *pvd; 1188 /* Tracks if there already is a cached copy. */ 1189 vd2 = 0; 1190 while (next) { 1191 vd = vd_ptr(next); 1192 vt_ = vb_ptr(vd->vb_start); 1193 if (vt_[0] != vt_size || 0 != memcmp(vt, vt_, vt_size)) { 1194 pvd = &vd->next; 1195 next = vd->next; 1196 continue; 1197 } 1198 /* Can't share emitted vtables between buffers, */ 1199 if (vd->nest_id != B->nest_id) { 1200 /* but we don't have to resubmit to cache. */ 1201 vd2 = vd; 1202 /* See if there is a better match. */ 1203 pvd = &vd->next; 1204 next = vd->next; 1205 continue; 1206 } 1207 /* Move to front hash strategy. */ 1208 if (pvd != pvd_head) { 1209 *pvd = vd->next; 1210 vd->next = *pvd_head; 1211 *pvd_head = next; 1212 } 1213 /* vtable exists and has been emitted within current buffer. */ 1214 return vd->vt_ref; 1215 } 1216 /* Allocate new descriptor. */ 1217 if (!(vd = reserve_buffer(B, flatcc_builder_alloc_vd, B->vd_end, sizeof(vtable_descriptor_t), 0))) { 1218 return 0; 1219 } 1220 next = B->vd_end; 1221 B->vd_end += (uoffset_t)sizeof(vtable_descriptor_t); 1222 1223 /* Identify the buffer this vtable descriptor belongs to. */ 1224 vd->nest_id = B->nest_id; 1225 1226 /* Move to front hash strategy. */ 1227 vd->next = *pvd_head; 1228 *pvd_head = next; 1229 if (0 == (vd->vt_ref = flatcc_builder_create_vtable(B, vt, vt_size))) { 1230 return 0; 1231 } 1232 if (vd2) { 1233 /* Reuse cached copy. */ 1234 vd->vb_start = vd2->vb_start; 1235 } else { 1236 if (B->vb_flush_limit && B->vb_flush_limit < B->vb_end + vt_size) { 1237 flatcc_builder_flush_vtable_cache(B); 1238 } else { 1239 /* Make space in vtable cache. */ 1240 if (!(vt_ = reserve_buffer(B, flatcc_builder_alloc_vb, B->vb_end, vt_size, 0))) { 1241 return -1; 1242 } 1243 vd->vb_start = B->vb_end; 1244 B->vb_end += vt_size; 1245 memcpy(vt_, vt, vt_size); 1246 } 1247 } 1248 return vd->vt_ref; 1249 } 1250 1251 flatcc_builder_ref_t flatcc_builder_create_table(flatcc_builder_t *B, const void *data, size_t size, uint16_t align, 1252 flatbuffers_voffset_t *offsets, int offset_count, flatcc_builder_vt_ref_t vt_ref) 1253 { 1254 int i; 1255 uoffset_t pad, vt_offset, vt_offset_field, vt_base, base, offset, *offset_field; 1256 iov_state_t iov; 1257 1258 check(offset_count >= 0, "expected non-negative offset_count"); 1259 /* 1260 * vtable references are offset by 1 to avoid confusion with 1261 * 0 as an error reference. It also uniquely identifies them 1262 * as vtables being the only uneven reference type. 1263 */ 1264 check(vt_ref & 1, "invalid vtable referenc"); 1265 get_min_align(&align, field_size); 1266 set_min_align(B, align); 1267 /* Alignment is calculated for the first element, not the header. */ 1268 pad = front_pad(B, (uoffset_t)size, align); 1269 base = (uoffset_t)B->emit_start - (uoffset_t)(pad + size + field_size); 1270 /* Adjust by 1 to get unencoded vtable reference. */ 1271 vt_base = (uoffset_t)(vt_ref - 1); 1272 vt_offset = base - vt_base; 1273 /* Avoid overflow. */ 1274 if (base - vt_offset != vt_base) { 1275 return -1; 1276 } 1277 /* Protocol endian encoding. */ 1278 write_uoffset(&vt_offset_field, vt_offset); 1279 for (i = 0; i < offset_count; ++i) { 1280 offset_field = (uoffset_t *)((size_t)data + offsets[i]); 1281 offset = *offset_field - base - offsets[i] - (uoffset_t)field_size; 1282 write_uoffset(offset_field, offset); 1283 } 1284 init_iov(); 1285 push_iov(&vt_offset_field, field_size); 1286 push_iov(data, size); 1287 push_iov(_pad, pad); 1288 return emit_front(B, &iov); 1289 } 1290 1291 int flatcc_builder_check_required_field(flatcc_builder_t *B, flatbuffers_voffset_t id) 1292 { 1293 check(frame(type) == flatcc_builder_table, "expected table frame"); 1294 1295 return id < B->id_end && B->vs[id] != 0; 1296 } 1297 1298 int flatcc_builder_check_union_field(flatcc_builder_t *B, flatbuffers_voffset_t id) 1299 { 1300 check(frame(type) == flatcc_builder_table, "expected table frame"); 1301 1302 if (id == 0 || id >= B->id_end) { 1303 return 0; 1304 } 1305 if (B->vs[id - 1] == 0) { 1306 return B->vs[id] == 0; 1307 } 1308 if (*(uint8_t *)(B->ds + B->vs[id - 1])) { 1309 return B->vs[id] != 0; 1310 } 1311 return B->vs[id] == 0; 1312 } 1313 1314 int flatcc_builder_check_required(flatcc_builder_t *B, const flatbuffers_voffset_t *required, int count) 1315 { 1316 int i; 1317 1318 check(frame(type) == flatcc_builder_table, "expected table frame"); 1319 1320 if (B->id_end < count) { 1321 return 0; 1322 } 1323 for (i = 0; i < count; ++i) { 1324 if (B->vs[required[i]] == 0) { 1325 return 0; 1326 } 1327 } 1328 return 1; 1329 } 1330 1331 flatcc_builder_ref_t flatcc_builder_end_table(flatcc_builder_t *B) 1332 { 1333 voffset_t *vt, vt_size; 1334 flatcc_builder_ref_t table_ref, vt_ref; 1335 int pl_count; 1336 voffset_t *pl; 1337 size_t tsize; 1338 1339 check(frame(type) == flatcc_builder_table, "expected table frame"); 1340 1341 /* We have `ds_limit`, so we should not have to check for overflow here. */ 1342 1343 vt = B->vs - 2; 1344 vt_size = (voffset_t)(sizeof(voffset_t) * (B->id_end + 2u)); 1345 /* Update vtable header fields, first vtable size, then object table size. */ 1346 vt[0] = vt_size; 1347 /* 1348 * The `ds` buffer is always at least `field_size` aligned but excludes the 1349 * initial vtable offset field. Therefore `field_size` is added here 1350 * to the total table size in the vtable. 1351 */ 1352 tsize = (size_t)(B->ds_offset + field_size); 1353 /* 1354 * Tables are limited to 64K in standard FlatBuffers format due to the voffset 1355 * 16 bit size, but we must also be able to store the table size, so the 1356 * table payload has to be slightly less than that. 1357 */ 1358 check(tsize <= FLATBUFFERS_VOFFSET_MAX, "table too large"); 1359 vt[1] = (voffset_t)tsize; 1360 FLATCC_BUILDER_UPDATE_VT_HASH(B->vt_hash, (uint32_t)vt[0], (uint32_t)vt[1]); 1361 /* Find already emitted vtable, or emit a new one. */ 1362 if (!(vt_ref = flatcc_builder_create_cached_vtable(B, vt, vt_size, B->vt_hash))) { 1363 return 0; 1364 } 1365 /* Clear vs stack so it is ready for the next vtable (ds stack is cleared by exit frame). */ 1366 memset(vt, 0, vt_size); 1367 1368 pl = pl_ptr(frame(container.table.pl_end)); 1369 pl_count = (int)(B->pl - pl); 1370 if (0 == (table_ref = flatcc_builder_create_table(B, B->ds, B->ds_offset, B->align, pl, pl_count, vt_ref))) { 1371 return 0; 1372 } 1373 B->vt_hash = frame(container.table.vt_hash); 1374 B->id_end = frame(container.table.id_end); 1375 B->vs = vs_ptr(frame(container.table.vs_end)); 1376 B->pl = pl_ptr(frame(container.table.pl_end)); 1377 exit_frame(B); 1378 return table_ref; 1379 } 1380 1381 flatcc_builder_ref_t flatcc_builder_create_vector(flatcc_builder_t *B, 1382 const void *data, size_t count, size_t elem_size, uint16_t align, size_t max_count) 1383 { 1384 /* 1385 * Note: it is important that vec_size is uoffset not size_t 1386 * in case sizeof(uoffset_t) > sizeof(size_t) because max_count is 1387 * defined in terms of uoffset_t representation size, and also 1388 * because we risk accepting too large a vector even if max_count is 1389 * not violated. 1390 */ 1391 uoffset_t vec_size, vec_pad, length_prefix; 1392 iov_state_t iov; 1393 1394 check_error(count <= max_count, 0, "vector max_count violated"); 1395 get_min_align(&align, field_size); 1396 set_min_align(B, align); 1397 vec_size = (uoffset_t)count * (uoffset_t)elem_size; 1398 /* 1399 * That can happen on 32 bit systems when uoffset_t is defined as 64-bit. 1400 * `emit_front/back` captures overflow, but not if our size type wraps first. 1401 */ 1402 #if FLATBUFFERS_UOFFSET_MAX > SIZE_MAX 1403 check_error(vec_size < SIZE_MAX, 0, "vector larger than address space"); 1404 #endif 1405 write_uoffset(&length_prefix, (uoffset_t)count); 1406 /* Alignment is calculated for the first element, not the header. */ 1407 vec_pad = front_pad(B, vec_size, align); 1408 init_iov(); 1409 push_iov(&length_prefix, field_size); 1410 push_iov(data, vec_size); 1411 push_iov(_pad, vec_pad); 1412 return emit_front(B, &iov); 1413 } 1414 1415 /* 1416 * Note: FlatBuffers official documentation states that the size field of a 1417 * vector is a 32-bit element count. It is not quite clear if the 1418 * intention is to have the size field be of type uoffset_t since tables 1419 * also have a uoffset_t sized header, or if the vector size should 1420 * remain unchanged if uoffset is changed to 16- or 64-bits 1421 * respectively. Since it makes most sense to have a vector compatible 1422 * with the addressable space, we choose to use uoffset_t as size field, 1423 * which remains compatible with the default 32-bit version of uoffset_t. 1424 */ 1425 flatcc_builder_ref_t flatcc_builder_end_vector(flatcc_builder_t *B) 1426 { 1427 flatcc_builder_ref_t vector_ref; 1428 1429 check(frame(type) == flatcc_builder_vector, "expected vector frame"); 1430 1431 if (0 == (vector_ref = flatcc_builder_create_vector(B, B->ds, 1432 frame(container.vector.count), frame(container.vector.elem_size), 1433 B->align, frame(container.vector.max_count)))) { 1434 return 0; 1435 } 1436 exit_frame(B); 1437 return vector_ref; 1438 } 1439 1440 size_t flatcc_builder_vector_count(flatcc_builder_t *B) 1441 { 1442 return frame(container.vector.count); 1443 } 1444 1445 void *flatcc_builder_vector_edit(flatcc_builder_t *B) 1446 { 1447 return B->ds; 1448 } 1449 1450 /* This function destroys the source content but avoids stack allocation. */ 1451 static flatcc_builder_ref_t _create_offset_vector_direct(flatcc_builder_t *B, 1452 flatcc_builder_ref_t *vec, size_t count, const utype_t *types) 1453 { 1454 uoffset_t vec_size, vec_pad; 1455 uoffset_t length_prefix, offset; 1456 uoffset_t i; 1457 soffset_t base; 1458 iov_state_t iov; 1459 1460 if ((uoffset_t)count > max_offset_count) { 1461 return 0; 1462 } 1463 set_min_align(B, field_size); 1464 vec_size = (uoffset_t)(count * field_size); 1465 write_uoffset(&length_prefix, (uoffset_t)count); 1466 /* Alignment is calculated for the first element, not the header. */ 1467 vec_pad = front_pad(B, vec_size, field_size); 1468 init_iov(); 1469 push_iov(&length_prefix, field_size); 1470 push_iov(vec, vec_size); 1471 push_iov(_pad, vec_pad); 1472 base = B->emit_start - (soffset_t)iov.len; 1473 for (i = 0; i < (uoffset_t)count; ++i) { 1474 /* 1475 * 0 is either end of buffer, start of vtables, or start of 1476 * buffer depending on the direction in which the buffer is 1477 * built. None of these can create a valid 0 reference but it 1478 * is easy to create by mistake when manually building offset 1479 * vectors. 1480 * 1481 * Unions do permit nulls, but only when the type is NONE. 1482 */ 1483 if (vec[i] != 0) { 1484 offset = (uoffset_t) 1485 (vec[i] - base - (soffset_t)(i * field_size) - (soffset_t)field_size); 1486 write_uoffset(&vec[i], offset); 1487 if (types) { 1488 check(types[i] != 0, "union vector cannot have non-null element with type NONE"); 1489 } 1490 } else { 1491 if (types) { 1492 check(types[i] == 0, "union vector cannot have null element without type NONE"); 1493 } else { 1494 check(0, "offset vector cannot have null element"); 1495 } 1496 } 1497 } 1498 return emit_front(B, &iov); 1499 } 1500 1501 flatcc_builder_ref_t flatcc_builder_create_offset_vector_direct(flatcc_builder_t *B, 1502 flatcc_builder_ref_t *vec, size_t count) 1503 { 1504 return _create_offset_vector_direct(B, vec, count, 0); 1505 } 1506 1507 flatcc_builder_ref_t flatcc_builder_end_offset_vector(flatcc_builder_t *B) 1508 { 1509 flatcc_builder_ref_t vector_ref; 1510 1511 check(frame(type) == flatcc_builder_offset_vector, "expected offset vector frame"); 1512 if (0 == (vector_ref = flatcc_builder_create_offset_vector_direct(B, 1513 (flatcc_builder_ref_t *)B->ds, frame(container.vector.count)))) { 1514 return 0; 1515 } 1516 exit_frame(B); 1517 return vector_ref; 1518 } 1519 1520 flatcc_builder_ref_t flatcc_builder_end_offset_vector_for_unions(flatcc_builder_t *B, const utype_t *types) 1521 { 1522 flatcc_builder_ref_t vector_ref; 1523 1524 check(frame(type) == flatcc_builder_offset_vector, "expected offset vector frame"); 1525 if (0 == (vector_ref = _create_offset_vector_direct(B, 1526 (flatcc_builder_ref_t *)B->ds, frame(container.vector.count), types))) { 1527 return 0; 1528 } 1529 exit_frame(B); 1530 return vector_ref; 1531 } 1532 1533 void *flatcc_builder_offset_vector_edit(flatcc_builder_t *B) 1534 { 1535 return B->ds; 1536 } 1537 1538 size_t flatcc_builder_offset_vector_count(flatcc_builder_t *B) 1539 { 1540 return frame(container.vector.count); 1541 } 1542 1543 int flatcc_builder_table_add_union(flatcc_builder_t *B, int id, 1544 flatcc_builder_union_ref_t uref) 1545 { 1546 flatcc_builder_ref_t *pref; 1547 flatcc_builder_utype_t *putype; 1548 1549 check(frame(type) == flatcc_builder_table, "expected table frame"); 1550 check_error(uref.type != 0 || uref.value == 0, -1, "expected null value for type NONE"); 1551 if (uref.value != 0) { 1552 pref = flatcc_builder_table_add_offset(B, id); 1553 check_error(pref != 0, -1, "unable to add union value"); 1554 *pref = uref.value; 1555 } 1556 putype = flatcc_builder_table_add(B, id - 1, utype_size, utype_size); 1557 check_error(putype != 0, -1, "unable to add union type"); 1558 write_utype(putype, uref.type); 1559 return 0; 1560 } 1561 1562 int flatcc_builder_table_add_union_vector(flatcc_builder_t *B, int id, 1563 flatcc_builder_union_vec_ref_t uvref) 1564 { 1565 flatcc_builder_ref_t *pref; 1566 1567 check(frame(type) == flatcc_builder_table, "expected table frame"); 1568 check_error((uvref.type == 0) == (uvref.value == 0), -1, "expected both type and value vector, or neither"); 1569 if (uvref.type != 0) { 1570 pref = flatcc_builder_table_add_offset(B, id - 1); 1571 check_error(pref != 0, -1, "unable to add union member"); 1572 *pref = uvref.type; 1573 1574 pref = flatcc_builder_table_add_offset(B, id); 1575 check_error(pref != 0, -1, "unable to add union member"); 1576 *pref = uvref.value; 1577 } 1578 return 0; 1579 } 1580 1581 flatcc_builder_union_vec_ref_t flatcc_builder_create_union_vector(flatcc_builder_t *B, 1582 const flatcc_builder_union_ref_t *urefs, size_t count) 1583 { 1584 flatcc_builder_union_vec_ref_t uvref = { 0, 0 }; 1585 flatcc_builder_utype_t *types; 1586 flatcc_builder_ref_t *refs; 1587 size_t i; 1588 1589 if (flatcc_builder_start_offset_vector(B)) { 1590 return uvref; 1591 } 1592 if (0 == flatcc_builder_extend_offset_vector(B, count)) { 1593 return uvref; 1594 } 1595 if (0 == (types = push_ds(B, (uoffset_t)(utype_size * count)))) { 1596 return uvref; 1597 } 1598 1599 /* Safe even if push_ds caused stack reallocation. */ 1600 refs = flatcc_builder_offset_vector_edit(B); 1601 1602 for (i = 0; i < count; ++i) { 1603 types[i] = urefs[i].type; 1604 refs[i] = urefs[i].value; 1605 } 1606 uvref = flatcc_builder_create_union_vector_direct(B, 1607 types, refs, count); 1608 /* No need to clean up after out temporary types vector. */ 1609 exit_frame(B); 1610 return uvref; 1611 } 1612 1613 flatcc_builder_union_vec_ref_t flatcc_builder_create_union_vector_direct(flatcc_builder_t *B, 1614 const flatcc_builder_utype_t *types, flatcc_builder_ref_t *data, size_t count) 1615 { 1616 flatcc_builder_union_vec_ref_t uvref = { 0, 0 }; 1617 1618 if (0 == (uvref.value = _create_offset_vector_direct(B, data, count, types))) { 1619 return uvref; 1620 } 1621 if (0 == (uvref.type = flatcc_builder_create_type_vector(B, types, count))) { 1622 return uvref; 1623 } 1624 return uvref; 1625 } 1626 1627 flatcc_builder_ref_t flatcc_builder_create_type_vector(flatcc_builder_t *B, 1628 const flatcc_builder_utype_t *types, size_t count) 1629 { 1630 return flatcc_builder_create_vector(B, types, count, 1631 utype_size, utype_size, max_utype_count); 1632 } 1633 1634 int flatcc_builder_start_union_vector(flatcc_builder_t *B) 1635 { 1636 if (enter_frame(B, field_size)) { 1637 return -1; 1638 } 1639 frame(container.vector.elem_size) = union_size; 1640 frame(container.vector.count) = 0; 1641 frame(type) = flatcc_builder_union_vector; 1642 refresh_ds(B, data_limit); 1643 return 0; 1644 } 1645 1646 flatcc_builder_union_vec_ref_t flatcc_builder_end_union_vector(flatcc_builder_t *B) 1647 { 1648 flatcc_builder_union_vec_ref_t uvref = { 0, 0 }; 1649 flatcc_builder_utype_t *types; 1650 flatcc_builder_union_ref_t *urefs; 1651 flatcc_builder_ref_t *refs; 1652 size_t i, count; 1653 1654 check(frame(type) == flatcc_builder_union_vector, "expected union vector frame"); 1655 1656 /* 1657 * We could split the union vector in-place, but then we would have 1658 * to deal with strict pointer aliasing rules which is not worthwhile 1659 * so we create a new offset and type vector on the stack. 1660 * 1661 * We assume the stack is sufficiently aligned as is. 1662 */ 1663 count = flatcc_builder_union_vector_count(B); 1664 if (0 == (refs = push_ds(B, (uoffset_t)(count * (utype_size + field_size))))) { 1665 return uvref; 1666 } 1667 types = (flatcc_builder_utype_t *)(refs + count); 1668 1669 /* Safe even if push_ds caused stack reallocation. */ 1670 urefs = flatcc_builder_union_vector_edit(B); 1671 1672 for (i = 0; i < count; ++i) { 1673 types[i] = urefs[i].type; 1674 refs[i] = urefs[i].value; 1675 } 1676 uvref = flatcc_builder_create_union_vector_direct(B, types, refs, count); 1677 /* No need to clean up after out temporary types vector. */ 1678 exit_frame(B); 1679 return uvref; 1680 } 1681 1682 void *flatcc_builder_union_vector_edit(flatcc_builder_t *B) 1683 { 1684 return B->ds; 1685 } 1686 1687 size_t flatcc_builder_union_vector_count(flatcc_builder_t *B) 1688 { 1689 return frame(container.vector.count); 1690 } 1691 1692 flatcc_builder_union_ref_t *flatcc_builder_extend_union_vector(flatcc_builder_t *B, size_t count) 1693 { 1694 if (vector_count_add(B, (uoffset_t)count, max_union_count)) { 1695 return 0; 1696 } 1697 return push_ds(B, (uoffset_t)(union_size * count)); 1698 } 1699 1700 int flatcc_builder_truncate_union_vector(flatcc_builder_t *B, size_t count) 1701 { 1702 check(frame(type) == flatcc_builder_union_vector, "expected union vector frame"); 1703 check_error(frame(container.vector.count) >= (uoffset_t)count, -1, "cannot truncate vector past empty"); 1704 frame(container.vector.count) -= (uoffset_t)count; 1705 unpush_ds(B, frame(container.vector.elem_size) * (uoffset_t)count); 1706 return 0; 1707 } 1708 1709 flatcc_builder_union_ref_t *flatcc_builder_union_vector_push(flatcc_builder_t *B, 1710 flatcc_builder_union_ref_t uref) 1711 { 1712 flatcc_builder_union_ref_t *p; 1713 1714 check(frame(type) == flatcc_builder_union_vector, "expected union vector frame"); 1715 if (frame(container.vector.count) == max_union_count) { 1716 return 0; 1717 } 1718 frame(container.vector.count) += 1; 1719 if (0 == (p = push_ds(B, union_size))) { 1720 return 0; 1721 } 1722 *p = uref; 1723 return p; 1724 } 1725 1726 flatcc_builder_union_ref_t *flatcc_builder_append_union_vector(flatcc_builder_t *B, 1727 const flatcc_builder_union_ref_t *urefs, size_t count) 1728 { 1729 check(frame(type) == flatcc_builder_union_vector, "expected union vector frame"); 1730 if (vector_count_add(B, (uoffset_t)count, max_union_count)) { 1731 return 0; 1732 } 1733 return push_ds_copy(B, urefs, (uoffset_t)(union_size * count)); 1734 } 1735 1736 flatcc_builder_ref_t flatcc_builder_create_string(flatcc_builder_t *B, const char *s, size_t len) 1737 { 1738 uoffset_t s_pad; 1739 uoffset_t length_prefix; 1740 iov_state_t iov; 1741 1742 if (len > max_string_len) { 1743 return 0; 1744 } 1745 write_uoffset(&length_prefix, (uoffset_t)len); 1746 /* Add 1 for zero termination. */ 1747 s_pad = front_pad(B, (uoffset_t)len + 1, field_size) + 1; 1748 init_iov(); 1749 push_iov(&length_prefix, field_size); 1750 push_iov(s, len); 1751 push_iov(_pad, s_pad); 1752 return emit_front(B, &iov); 1753 } 1754 1755 flatcc_builder_ref_t flatcc_builder_create_string_str(flatcc_builder_t *B, const char *s) 1756 { 1757 return flatcc_builder_create_string(B, s, strlen(s)); 1758 } 1759 1760 flatcc_builder_ref_t flatcc_builder_create_string_strn(flatcc_builder_t *B, const char *s, size_t max_len) 1761 { 1762 return flatcc_builder_create_string(B, s, strnlen(s, max_len)); 1763 } 1764 1765 flatcc_builder_ref_t flatcc_builder_end_string(flatcc_builder_t *B) 1766 { 1767 flatcc_builder_ref_t string_ref; 1768 1769 check(frame(type) == flatcc_builder_string, "expected string frame"); 1770 FLATCC_ASSERT(frame(container.vector.count) == B->ds_offset); 1771 if (0 == (string_ref = flatcc_builder_create_string(B, 1772 (const char *)B->ds, B->ds_offset))) { 1773 return 0; 1774 } 1775 exit_frame(B); 1776 return string_ref; 1777 } 1778 1779 char *flatcc_builder_string_edit(flatcc_builder_t *B) 1780 { 1781 return (char *)B->ds; 1782 } 1783 1784 size_t flatcc_builder_string_len(flatcc_builder_t *B) 1785 { 1786 return frame(container.vector.count); 1787 } 1788 1789 void *flatcc_builder_table_add(flatcc_builder_t *B, int id, size_t size, uint16_t align) 1790 { 1791 /* 1792 * We align the offset relative to the first table field, excluding 1793 * the header holding the vtable reference. On the stack, `ds_first` 1794 * is aligned to 8 bytes thanks to the `enter_frame` logic, and this 1795 * provides a safe way to update the fields on the stack, but here 1796 * we are concerned with the target buffer alignment. 1797 * 1798 * We could also have aligned relative to the end of the table which 1799 * would allow us to emit each field immediately, but it would be a 1800 * confusing user experience wrt. field ordering, and it would add 1801 * more variability to vtable layouts, thus reducing reuse, and 1802 * frequent emissions to external emitter interface would be 1803 * sub-optimal. Also, with that appoach, the vtable offsets would 1804 * have to be adjusted at table end. 1805 * 1806 * As we have it, each emit occur at table end, vector end, string 1807 * end, or buffer end, which might be helpful to various backend 1808 * processors. 1809 */ 1810 check(frame(type) == flatcc_builder_table, "expected table frame"); 1811 check(id >= 0 && id <= (int)FLATBUFFERS_ID_MAX, "table id out of range"); 1812 if (align > B->align) { 1813 B->align = align; 1814 } 1815 #if FLATCC_BUILDER_ALLOW_REPEAT_TABLE_ADD 1816 if (B->vs[id] != 0) { 1817 return B->ds + B->vs[id] - field_size; 1818 } 1819 #else 1820 if (B->vs[id] != 0) { 1821 check(0, "table field already set"); 1822 return 0; 1823 } 1824 #endif 1825 FLATCC_BUILDER_UPDATE_VT_HASH(B->vt_hash, (uint32_t)id, (uint32_t)size); 1826 return push_ds_field(B, (uoffset_t)size, align, (voffset_t)id); 1827 } 1828 1829 void *flatcc_builder_table_edit(flatcc_builder_t *B, size_t size) 1830 { 1831 check(frame(type) == flatcc_builder_table, "expected table frame"); 1832 1833 return B->ds + B->ds_offset - size; 1834 } 1835 1836 void *flatcc_builder_table_add_copy(flatcc_builder_t *B, int id, const void *data, size_t size, uint16_t align) 1837 { 1838 void *p; 1839 1840 if ((p = flatcc_builder_table_add(B, id, size, align))) { 1841 memcpy(p, data, size); 1842 } 1843 return p; 1844 } 1845 1846 flatcc_builder_ref_t *flatcc_builder_table_add_offset(flatcc_builder_t *B, int id) 1847 { 1848 check(frame(type) == flatcc_builder_table, "expected table frame"); 1849 check(id >= 0 && id <= (int)FLATBUFFERS_ID_MAX, "table id out of range"); 1850 #if FLATCC_BUILDER_ALLOW_REPEAT_TABLE_ADD 1851 if (B->vs[id] != 0) { 1852 return B->ds + B->vs[id] - field_size; 1853 } 1854 #else 1855 if (B->vs[id] != 0) { 1856 check(0, "table field already set"); 1857 return 0; 1858 } 1859 #endif 1860 FLATCC_BUILDER_UPDATE_VT_HASH(B->vt_hash, (uint32_t)id, (uint32_t)field_size); 1861 return push_ds_offset_field(B, (voffset_t)id); 1862 } 1863 1864 uint16_t flatcc_builder_push_buffer_alignment(flatcc_builder_t *B) 1865 { 1866 uint16_t old_min_align = B->min_align; 1867 1868 B->min_align = field_size; 1869 return old_min_align; 1870 } 1871 1872 void flatcc_builder_pop_buffer_alignment(flatcc_builder_t *B, uint16_t pushed_align) 1873 { 1874 set_min_align(B, pushed_align); 1875 } 1876 1877 uint16_t flatcc_builder_get_buffer_alignment(flatcc_builder_t *B) 1878 { 1879 return B->min_align; 1880 } 1881 1882 void flatcc_builder_set_vtable_clustering(flatcc_builder_t *B, int enable) 1883 { 1884 /* Inverted because we zero all memory in B on init. */ 1885 B->disable_vt_clustering = !enable; 1886 } 1887 1888 void flatcc_builder_set_block_align(flatcc_builder_t *B, uint16_t align) 1889 { 1890 B->block_align = align; 1891 } 1892 1893 int flatcc_builder_get_level(flatcc_builder_t *B) 1894 { 1895 return B->level; 1896 } 1897 1898 void flatcc_builder_set_max_level(flatcc_builder_t *B, int max_level) 1899 { 1900 B->max_level = max_level; 1901 if (B->limit_level < B->max_level) { 1902 B->limit_level = B->max_level; 1903 } 1904 } 1905 1906 size_t flatcc_builder_get_buffer_size(flatcc_builder_t *B) 1907 { 1908 return (size_t)(B->emit_end - B->emit_start); 1909 } 1910 1911 flatcc_builder_ref_t flatcc_builder_get_buffer_start(flatcc_builder_t *B) 1912 { 1913 return B->emit_start; 1914 } 1915 1916 flatcc_builder_ref_t flatcc_builder_get_buffer_end(flatcc_builder_t *B) 1917 { 1918 return B->emit_end; 1919 } 1920 1921 void flatcc_builder_set_vtable_cache_limit(flatcc_builder_t *B, size_t size) 1922 { 1923 B->vb_flush_limit = size; 1924 } 1925 1926 void flatcc_builder_set_identifier(flatcc_builder_t *B, const char identifier[identifier_size]) 1927 { 1928 set_identifier(identifier); 1929 } 1930 1931 enum flatcc_builder_type flatcc_builder_get_type(flatcc_builder_t *B) 1932 { 1933 return B->frame ? frame(type) : flatcc_builder_empty; 1934 } 1935 1936 enum flatcc_builder_type flatcc_builder_get_type_at(flatcc_builder_t *B, int level) 1937 { 1938 if (level < 1 || level > B->level) { 1939 return flatcc_builder_empty; 1940 } 1941 return B->frame[level - B->level].type; 1942 } 1943 1944 void *flatcc_builder_get_direct_buffer(flatcc_builder_t *B, size_t *size_out) 1945 { 1946 if (B->is_default_emitter) { 1947 return flatcc_emitter_get_direct_buffer(&B->default_emit_context, size_out); 1948 } else { 1949 if (size_out) { 1950 *size_out = 0; 1951 } 1952 } 1953 return 0; 1954 } 1955 1956 void *flatcc_builder_copy_buffer(flatcc_builder_t *B, void *buffer, size_t size) 1957 { 1958 /* User is allowed to call tentatively to see if there is support. */ 1959 if (!B->is_default_emitter) { 1960 return 0; 1961 } 1962 buffer = flatcc_emitter_copy_buffer(&B->default_emit_context, buffer, size); 1963 check(buffer, "default emitter declined to copy buffer"); 1964 return buffer; 1965 } 1966 1967 void *flatcc_builder_finalize_buffer(flatcc_builder_t *B, size_t *size_out) 1968 { 1969 void * buffer; 1970 size_t size; 1971 1972 size = flatcc_builder_get_buffer_size(B); 1973 1974 if (size_out) { 1975 *size_out = size; 1976 } 1977 1978 buffer = FLATCC_BUILDER_ALLOC(size); 1979 1980 if (!buffer) { 1981 check(0, "failed to allocated memory for finalized buffer"); 1982 goto done; 1983 } 1984 if (!flatcc_builder_copy_buffer(B, buffer, size)) { 1985 check(0, "default emitter declined to copy buffer"); 1986 FLATCC_BUILDER_FREE(buffer); 1987 buffer = 0; 1988 } 1989 done: 1990 if (!buffer && size_out) { 1991 *size_out = 0; 1992 } 1993 return buffer; 1994 } 1995 1996 void *flatcc_builder_finalize_aligned_buffer(flatcc_builder_t *B, size_t *size_out) 1997 { 1998 void * buffer; 1999 size_t align; 2000 size_t size; 2001 2002 size = flatcc_builder_get_buffer_size(B); 2003 2004 if (size_out) { 2005 *size_out = size; 2006 } 2007 align = flatcc_builder_get_buffer_alignment(B); 2008 2009 size = (size + align - 1) & ~(align - 1); 2010 buffer = FLATCC_BUILDER_ALIGNED_ALLOC(align, size); 2011 2012 if (!buffer) { 2013 goto done; 2014 } 2015 if (!flatcc_builder_copy_buffer(B, buffer, size)) { 2016 FLATCC_BUILDER_ALIGNED_FREE(buffer); 2017 buffer = 0; 2018 goto done; 2019 } 2020 done: 2021 if (!buffer && size_out) { 2022 *size_out = 0; 2023 } 2024 return buffer; 2025 } 2026 2027 void *flatcc_builder_aligned_alloc(size_t alignment, size_t size) 2028 { 2029 return FLATCC_BUILDER_ALIGNED_ALLOC(alignment, size); 2030 } 2031 2032 void flatcc_builder_aligned_free(void *p) 2033 { 2034 FLATCC_BUILDER_ALIGNED_FREE(p); 2035 } 2036 2037 void *flatcc_builder_alloc(size_t size) 2038 { 2039 return FLATCC_BUILDER_ALLOC(size); 2040 } 2041 2042 void flatcc_builder_free(void *p) 2043 { 2044 FLATCC_BUILDER_FREE(p); 2045 } 2046 2047 void *flatcc_builder_get_emit_context(flatcc_builder_t *B) 2048 { 2049 return B->emit_context; 2050 }