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