-
Notifications
You must be signed in to change notification settings - Fork 220
Expand file tree
/
Copy pathflatcc_builder.h
More file actions
1911 lines (1749 loc) · 78.2 KB
/
flatcc_builder.h
File metadata and controls
1911 lines (1749 loc) · 78.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifndef FLATCC_BUILDER_H
#define FLATCC_BUILDER_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Library for building untyped FlatBuffers. Intended as a support
* library for generated C code to produce typed builders, but might
* also be useful in runtime environments and as support for scripting
* languages.
*
* The builder has two API layers: a stack based `start/end` approach,
* and a direct `create`, and they may be mixed freely. The direct
* approach may be used as part of more specialized optimizations such
* as rewriting buffers while the stack approach is convenient for state
* machine driven parsers without a stack, or with a very simple stack
* without extra allocations.
*
* The builder emits partial buffer sequences to a user provided emitter
* function and does not require a full buffer reprensenation in memory.
* For this reason it also does not support sorting or other operations
* that requires representing the buffer, but post-processors can easily
* do this, and the generated schema specific code and provide functions
* to handle this.
*
* A custom allocator with a default realloc implementation can place
* restraints on resource consumption and provide initial allocation
* sizes for various buffers and stacks in use.
*
* A buffer under construction uses a virtual address space for the
* completed part of the buffer, starting at 0 and growing in both
* directions, or just down depending on whether vtables should be
* clustered at the end or not. Clustering may help caching and
* preshipping that part of the buffer.
*
* Because an offset cannot be known before its reference location is
* defined, every completed table, vector, etc. returns a reference into
* the virtual address range. If the final buffer keeps the 0 offset,
* these references remain stable an may be used for external references
* into the buffer.
*
* The maximum buffer that can be constructed is in praxis limited to
* half the UOFFSET_MAX size, typically 2^31 bytes, not counting
* clustered vtables that may consume and additional 2^31 bytes
* (positive address range), but in praxis cannot because vtable
* references are signed and thus limited to 2^31 bytes (or equivalent
* depending on the flatbuffer types chosen).
*
* CORRECTION: in various places rules are mentioned about nesting and using
* a reference at most once. In fact, DAG's are also valid flatbuffers.
* This means a reference may be reused as long as each individual use
* obeys the rules and, for example, circular references are not
* constructed (circular types are ok, but objects graphs with cycles
* are not permitted). Be especially aware of the offset vector create
* call which translates the references into offsets - this can be
* reverted by noting the reference in vector and calculate the base
* used for the offset to restore the original references after the
* vector has been emitted.
*/
#include <stdlib.h>
#ifndef UINT8_MAX
#include <stdint.h>
#endif
#include "flatcc_flatbuffers.h"
#include "flatcc_emitter.h"
#include "flatcc_refmap.h"
/* It is possible to enable logging here. */
#ifndef FLATCC_BUILDER_ASSERT
#define FLATCC_BUILDER_ASSERT(cond, reason) FLATCC_ASSERT(cond)
#endif
/*
* Eror handling is not convenient and correct use should not cause
* errors beyond possibly memory allocation, but assertions are a
* good way to trace problems.
*
* Note: some internal assertion will remain if disabled.
*/
#ifndef FLATCC_BUILDER_ASSERT_ON_ERROR
#define FLATCC_BUILDER_ASSERT_ON_ERROR 1
#endif
/*
* If set, checks user input agains state and returns error,
* otherwise errors are ignored (assuming they won't happen).
* Errors will be asserted if enabled and checks are not skipped.
*/
#ifndef FLATCC_BUILDER_SKIP_CHECKS
#define FLATCC_BUILDER_SKIP_CHECKS 0
#endif
/*
* When adding the same field to a table twice this is either an error
* or the existing field is returned, potentially introducing garbage
* if the type is a vector, table, or string. When implementing parsers
* it may be convenient to not treat this as an error.
*/
#ifndef FLATCC_BUILDER_ALLOW_REPEAT_TABLE_ADD
#define FLATCC_BUILDER_ALLOW_REPEAT_TABLE_ADD 0
#endif
/**
* This type must have same size as `flatbuffers_uoffset_t`
* and must be a signed type.
*/
typedef flatbuffers_soffset_t flatcc_builder_ref_t;
typedef flatbuffers_utype_t flatcc_builder_utype_t;
/**
* This type must be compatible with code generation that
* creates union specific ref types.
*/
typedef struct flatcc_builder_union_ref {
flatcc_builder_utype_t type;
flatcc_builder_ref_t value;
} flatcc_builder_union_ref_t;
typedef struct flatcc_builder_union_vec_ref {
flatcc_builder_ref_t type;
flatcc_builder_ref_t value;
} flatcc_builder_union_vec_ref_t;
/**
* Virtual tables are off by one to avoid being mistaken for error at
* position 0, and it makes them detectable as such because no other
* reference is uneven. Vtables are emitted at their actual location
* which is one less than the reference value.
*/
typedef flatbuffers_soffset_t flatcc_builder_vt_ref_t;
typedef flatbuffers_uoffset_t flatcc_builder_identifier_t;
/**
* Hints to custom allocators so they can provide initial alloc sizes
* etc. There will be at most one buffer for each allocation type per
* flatcc_builder instance. Buffers containing only structs may avoid
* allocation altogether using a `create` call. The vs stack must hold
* vtable entries for all open tables up to their requested max id, but
* unused max id overlap on the stack. The final vtables only store the
* largest id actually added. The fs stack must hold stack frames for
* the nesting levels expected in the buffer, each about 50-100 bytes.
* The ds stack holds open vectors, table data, and nested buffer state.
* `create` calls bypass the `ds` and `fs` stack and are thus faster.
* The vb buffer holds a copy of all vtables seen and emitted since last
* vtable flush. The patch log holds a uoffset for every table field
* added to currently open tables. The hash table holds a uoffset entry
* for each hash slot where the allocator decides how many to provide
* above a certain minimum. The vd buffer allocates vtable descriptors
* which is a reference to an emitted vtable, an offset to a cached
* vtable, and a link to next descriptor with same hash. Calling `reset`
* after build can either keep the allocation levels for the next
* buffer, or reduce the buffers already allocated by requesting 1 byte
* allocations (meaning provide a default).
*
* The user stack is not automatically allocated, but when entered
* explicitly, the boundary is rembered in the current live
* frame.
*/
enum flatcc_builder_alloc_type {
/* The stack where vtables are build. */
flatcc_builder_alloc_vs,
/* The stack where data structures are build. */
flatcc_builder_alloc_ds,
/* The virtual table buffer cache, holds a copy of each vt seen. */
flatcc_builder_alloc_vb,
/* The patch log, remembers table fields with outstanding offset refs. */
flatcc_builder_alloc_pl,
/* The stack of frames for nested types. */
flatcc_builder_alloc_fs,
/* The hash table part of the virtual table cache. */
flatcc_builder_alloc_ht,
/* The vtable descriptor buffer, i.e. list elements for emitted vtables. */
flatcc_builder_alloc_vd,
/* User stack frame for custom data. */
flatcc_builder_alloc_us,
/* Number of allocation buffers. */
flatcc_builder_alloc_buffer_count
};
/** Must reflect the `flatcc_builder_alloc_type` enum. */
#define FLATCC_BUILDER_ALLOC_BUFFER_COUNT flatcc_builder_alloc_buffer_count
#ifndef FLATCC_BUILDER_ALLOC
#define FLATCC_BUILDER_ALLOC(n) FLATCC_ALLOC(n)
#endif
#ifndef FLATCC_BUILDER_FREE
#define FLATCC_BUILDER_FREE(p) FLATCC_FREE(p)
#endif
#ifndef FLATCC_BUILDER_REALLOC
#define FLATCC_BUILDER_REALLOC(p, n) FLATCC_REALLOC(p, n)
#endif
#ifndef FLATCC_BUILDER_ALIGNED_ALLOC
#define FLATCC_BUILDER_ALIGNED_ALLOC(a, n) FLATCC_ALIGNED_ALLOC(a, n)
#endif
#ifndef FLATCC_BUILDER_ALIGNED_FREE
#define FLATCC_BUILDER_ALIGNED_FREE(p) FLATCC_ALIGNED_FREE(p)
#endif
/**
* Emits data to a conceptual deque by appending to either front or
* back, starting from offset 0.
*
* Each emit call appends a strictly later or earlier sequence than the
* last emit with same offset sign. Thus a buffer is gradually grown at
* both ends. `len` is the combined length of all iov entries such that
* `offset + len` yields the former offset for negative offsets and
* `offset + len` yields the next offset for non-negative offsets.
* The bulk of the data will be in the negative range, possibly all of
* it. The first emitted emitted range will either start or end at
* offset 0. If offset 0 is emitted, it indicates the start of clustered
* vtables. The last positive (non-zero) offset may be zero padding to
* place the buffer in a full multiple of `block_align`, if set.
*
* No iov entry is empty, 0 < iov_count <= FLATCC_IOV_COUNT_MAX.
*
* The source data are in general ephemeral and should be consumed
* immediately, as opposed to caching iov.
*
* For high performance applications:
*
* The `create` calls may reference longer living data, but header
* fields etc. will still be short lived. If an emitter wants to
* reference data in another buffer rather than copying, it should
* inspect the memory range. The length of an iov entry may also be used
* since headers are never very long (anything starting at 16 bytes can
* safely be assumed to be user provided, or static zero padding). It is
* guaranteed that data pointers in `create` calls receive a unique slot
* separate from temporary headers, in the iov table which may be used
* for range checking or hashing (`create_table` is the only call that
* mutates the data buffer). It is also guaranteed (with the exception
* of `create_table` and `create_cached_vtable`) that data provided to
* create calls are not referenced at all by the builder, and these data
* may therefore de-facto be handles rather than direct pointers when
* the emitter and data provider can agree on such a protocol. This does
* NOT apply to any start/end/add/etc. calls which do copy to stack.
* `flatcc_builder_padding_base` may be used to test if an iov entry is
* zero padding which always begins at that address.
*
* Future: the emit interface could be extended with a type code
* and return an existing object insted of the emitted if, for
* example, they are identical. Outside this api level, generated
* code could provide a table comparison function to help such
* deduplication. It would be optional because two equal objects
* are not necessarily identical. The emitter already receives
* one object at time.
*
* Returns 0 on success and otherwise causes the flatcc_builder
* to fail.
*/
typedef int flatcc_builder_emit_fun(void *emit_context,
const flatcc_iovec_t *iov, int iov_count, flatbuffers_soffset_t offset, size_t len);
/*
* Returns a pointer to static padding used in emitter calls. May
* sometimes also be used for empty defaults such as identifier.
*/
extern const uint8_t flatcc_builder_padding_base[];
/**
* `request` is a minimum size to be returned, but allocation is
* expected to grow exponentially or in reasonable chunks. Notably,
* `alloc_type = flatcc_builder_alloc_ht` will only use highest available
* power of 2. The allocator may shrink if `request` is well below
* current size but should avoid repeated resizing on small changes in
* request sizes. If `zero_fill` is non-zero, allocated data beyond
* the current size must be zeroed. The buffer `b` may be null with 0
* length initially. `alloc_context` is completely implementation
* dependendent, and not needed when just relying on realloc. The
* resulting buffer may be the same or different with moved data, like
* realloc. Returns -1 with unmodified buffer on failure or 0 on
* success. The `alloc_type` identifies the buffer type. This may be
* used to cache buffers between instances of builders, or to decide a
* default allocation size larger than requested. If `need` is zero the
* buffer should be deallocate if non-zero, and return success (0)
* regardless.
*/
typedef int flatcc_builder_alloc_fun(void *alloc_context,
flatcc_iovec_t *b, size_t request, int zero_fill, int alloc_type);
/*
* The number of hash slots there will be allocated space for. The
* allocator may provide more. The size returned should be
* `sizeof(flatbuffers_uoffset_t) * count`, where the size is a power of
* 2 (or the rest is wasted). The hash table can store many more entries
* than slots using linear search. The table does not resize.
*/
#ifndef FLATCC_BUILDER_MIN_HASH_COUNT
#define FLATCC_BUILDER_MIN_HASH_COUNT 64
#endif
typedef struct __flatcc_builder_buffer_frame __flatcc_builder_buffer_frame_t;
struct __flatcc_builder_buffer_frame {
flatcc_builder_identifier_t identifier;
flatcc_builder_ref_t mark;
flatbuffers_uoffset_t vs_end;
flatbuffers_uoffset_t nest_id;
uint16_t flags;
uint16_t block_align;
};
typedef struct __flatcc_builder_vector_frame __flatcc_builder_vector_frame_t;
struct __flatcc_builder_vector_frame {
flatbuffers_uoffset_t elem_size;
flatbuffers_uoffset_t count;
flatbuffers_uoffset_t max_count;
};
typedef struct __flatcc_builder_table_frame __flatcc_builder_table_frame_t;
struct __flatcc_builder_table_frame {
flatbuffers_uoffset_t vs_end;
flatbuffers_uoffset_t pl_end;
uint32_t vt_hash;
flatbuffers_voffset_t id_end;
};
/*
* Store state for nested structures such as buffers, tables and vectors.
*
* For less busy data and data where access to a previous state is
* irrelevant, the frame may store the current state directly. Otherwise
* the current state is maintained in the flatcc_builder_t structure in a
* possibly derived form (e.g. ds pointer instead of ds_end offset) and
* the frame is used to store the previous state when the frame is
* entered.
*
* Most operations have a start/update/end cycle the decides the
* liftetime of a frame, but these generally also have a direct form
* (create) that does not use a frame at all. These still do some
* state updates notably passing min_align to parent which may also be
* an operation without a frame following the child level operation
* (e.g. create struct, create buffer). Ending a frame results in the
* same kind of updates.
*/
typedef struct __flatcc_builder_frame __flatcc_builder_frame_t;
struct __flatcc_builder_frame {
flatbuffers_uoffset_t ds_first;
flatbuffers_uoffset_t type_limit;
flatbuffers_uoffset_t ds_offset;
uint16_t align;
uint16_t type;
union {
__flatcc_builder_table_frame_t table;
__flatcc_builder_vector_frame_t vector;
__flatcc_builder_buffer_frame_t buffer;
} container;
};
/**
* The main flatcc_builder structure. Can be stack allocated and must
* be initialized with `flatcc_builder_init` and cleared with
* `flatcc_builder_clear` to reclaim memory. Between buffer builds,
* `flatcc_builder_reset` may be used.
*/
typedef struct flatcc_builder flatcc_builder_t;
struct flatcc_builder {
/* Next entry on reserved stack in `alloc_pl` buffer. */
flatbuffers_voffset_t *pl;
/* Next entry on reserved stack in `alloc_vs` buffer. */
flatbuffers_voffset_t *vs;
/* One above the highest entry in vs, used to track vt_size. */
flatbuffers_voffset_t id_end;
/* The evolving vtable hash updated with every new field. */
uint32_t vt_hash;
/* Pointer to ds_first. */
uint8_t *ds;
/* Offset from `ds` on current frame. */
flatbuffers_uoffset_t ds_offset;
/* ds buffer size relative to ds_first, clamped to max size of current type. */
flatbuffers_uoffset_t ds_limit;
/* ds_first, ds_first + ds_offset is current ds stack range. */
flatbuffers_uoffset_t ds_first;
/* Points to currently open frame in `alloc_fs` buffer. */
__flatcc_builder_frame_t *frame;
/* Only significant to emitter function, if at all. */
void *emit_context;
/* Only significant to allocator function, if at all. */
void *alloc_context;
/* Customizable write function that both appends and prepends data. */
flatcc_builder_emit_fun *emit;
/* Customizable allocator that also deallocates. */
flatcc_builder_alloc_fun *alloc;
/* Buffers indexed by `alloc_type` */
flatcc_iovec_t buffers[FLATCC_BUILDER_ALLOC_BUFFER_COUNT];
/* Number of slots in ht given as 1 << ht_width. */
size_t ht_width;
/* The location in vb to add next cached vtable. */
flatbuffers_uoffset_t vb_end;
/* Where to allocate next vtable descriptor for hash table. */
flatbuffers_uoffset_t vd_end;
/* Ensure final buffer is aligned to at least this. Nested buffers get their own `min_align`. */
uint16_t min_align;
/* The current active objects alignment isolated from nested activity. */
uint16_t align;
/* The current buffers block alignment used when emitting buffer. */
uint16_t block_align;
/* Signed virtual address range used for `flatcc_builder_ref_t` and emitter. */
flatcc_builder_ref_t emit_start;
flatcc_builder_ref_t emit_end;
/* 0 for top level, and end of buffer ref for nested buffers (can also be 0). */
flatcc_builder_ref_t buffer_mark;
/* Next nest_id. */
flatbuffers_uoffset_t nest_count;
/* Unique id to prevent sharing of vtables across buffers. */
flatbuffers_uoffset_t nest_id;
/* Current nesting level. Helpful to state-machines with explicit stack and to check `max_level`. */
int level;
/* Aggregate check for allocated frame and max_level. */
int limit_level;
/* Track size prefixed buffer. */
uint16_t buffer_flags;
/* Settings that may happen with no frame allocated. */
flatcc_builder_identifier_t identifier;
/* Settings that survive reset (emitter, alloc, and contexts also survive): */
/* If non-zero, vtable cache gets flushed periodically. */
size_t vb_flush_limit;
/* If non-zero, fails on deep nesting to help drivers with a stack, such as recursive parsers etc. */
int max_level;
/* If non-zero, do not cluster vtables at end, only emit negative offsets (0 by default). */
int disable_vt_clustering;
/* Set if the default emitter is being used. */
int is_default_emitter;
/* Only used with default emitter. */
flatcc_emitter_t default_emit_context;
/* Offset to the last entered user frame on the user frame stack, after frame header, or 0. */
size_t user_frame_offset;
/* The offset to the end of the most recent user frame. */
size_t user_frame_end;
/* The optional user supplied refmap for cloning DAG's - not shared with nested buffers. */
flatcc_refmap_t *refmap;
};
/**
* Call this before any other API call.
*
* The emitter handles the completed chunks of the buffer that will no
* longer be required by the builder. It is largely a `write` function
* that can append to both positive and negative offsets.
*
* No memory is allocated during init. Buffers will be allocated as
* needed. The `emit_context` is only used by the emitter, if at all.
*
* `flatcc_builder_reset/clear` calls are automtically forwarded to the
* default emitter.
*
* Returns -1 on failure, 0 on success.
*/
int flatcc_builder_init(flatcc_builder_t *B);
/**
* Use instead of `flatcc_builder_init` when providing a custom allocator
* or emitter. Leave emitter or allocator null to use default.
* Cleanup of emit and alloc context must be handled manually after
* the builder is cleared or reset, except if emitter is null the
* default will be automatically cleared and reset.
*
* Returns -1 on failure, 0 on success.
*/
int flatcc_builder_custom_init(flatcc_builder_t *B,
flatcc_builder_emit_fun *emit, void *emit_context,
flatcc_builder_alloc_fun *alloc, void *alloc_context);
/*
* Returns (flatcc_emitter_t *) if the default context is used.
* Other emitter might have null contexts.
*/
void *flatcc_builder_get_emit_context(flatcc_builder_t *B);
/**
* Prepares builder for a new build. The emitter is not told when a
* buffer is finished or when a new begins, and must be told so
* separately. Allocated buffers will be zeroed, but may optionally be
* reduced to their defaults (signalled by reallocating each non-empty
* buffer to a single byte). General settings are cleared optionally,
* such as cache flushing. Buffer specific settings such as buffer
* identifier are always cleared.
*
* Returns -1 if allocator complains during buffer reduction, 0 on
* success.
*/
int flatcc_builder_custom_reset(flatcc_builder_t *B,
int reduce_buffers, int set_defaults);
/*
* Same as `flatcc_builder_custom_reset` with default arguments
* where buffers are not reduced and default settings are not reset.
*/
int flatcc_builder_reset(flatcc_builder_t *B);
/**
* Deallocates all memory by calling allocate with a zero size request
* on each buffer, then zeroing the builder structure itself.
*/
void flatcc_builder_clear(flatcc_builder_t *B);
/**
* Allocates to next higher power of 2 using system realloc and ignores
* `alloc_context`. Only reduces size if a small subsequent increase in
* size would not trigger a reallocation. `alloc_type` is used to
* set minimum sizes. Hash tables are allocated to the exact requested
* size. See also `alloc_fun`.
*/
int flatcc_builder_default_alloc(void *alloc_context,
flatcc_iovec_t *b, size_t request, int zero_fill, int alloc_type);
/**
* If non-zero, the vtable cache will get flushed whenever it reaches
* the given limit at a point in time where more space is needed. The
* limit is not exact as it is only tested when reallocation is
* required.
*/
void flatcc_builder_set_vtable_cache_limit(flatcc_builder_t *B, size_t size);
/**
* Manual flushing of vtable for long running tasks. Mostly used
* internally to deal with nested buffers.
*/
void flatcc_builder_flush_vtable_cache(flatcc_builder_t *B);
/**
* Low-level support function to aid in constructing nested buffers without
* allocation. Not for regular use.
*
* Call where `start_buffer` would have been placed when using
* `create_buffer` in a nested context. Save the return value on a stack
* as argument to `pop_buffer_alignment`.
*
* The call resets the current derived buffer alignment so the nested
* buffer will not be aligned to more than required.
*
* Often it will not be necessary to be so careful with alignment since
* the alignment cannot be invalid by failing to use push and pop, but
* for code generation it will ensure the correct result every time.
*/
uint16_t flatcc_builder_push_buffer_alignment(flatcc_builder_t *B);
/**
* Low-level call.
*
* Call with the return value from push_buffer_alignment after a nested
* `create_buffer_call`. The alignments merge back up in the buffer
* hierarchy so the top level buffer gets the largest of all aligments.
*/
void flatcc_builder_pop_buffer_alignment(flatcc_builder_t *B, uint16_t buffer_align);
/**
* This value may be of interest when the buffer has been ended, for
* example when subsequently allocating memory for the buffer to ensure
* that memory is properly aligned.
*/
uint16_t flatcc_builder_get_buffer_alignment(flatcc_builder_t *B);
/**
* Level 0 means no buffer is started, otherwise it increments with
* start calls and decrements with end calls (approximately for
* optimized operations such as table vectors).
*
* If `max_level` has been set, `get_level` always returns a value <=
* `max_level` provided no start call has failed.
*
* Level continues to increment inside nested buffers.
*/
int flatcc_builder_get_level(flatcc_builder_t *B);
/**
* Setting the max level triggers a failure on start of new nestings
* when the level is reached. May be used to protect recursive descend
* parsers etc. or later buffer readers.
*
* The builder itself is not sensitive to depth, and the allocator is a
* better way to protect resource abuse.
*
* `max_level` is not reset inside nested buffers.
*/
void flatcc_builder_set_max_level(flatcc_builder_t *B, int level);
/**
* By default ordinary data such as tables are placed in front of
* earlier produced content and vtables are placed at the very end thus
* clustering vtables together. This can be disabled so all content is
* placed in front. Nested buffers ignores this setting because they can
* only place content in front because they cannot blend with the
* containing buffers content. Clustering could be more cache friendly
* and also enables pre-shipping of the vtables during transmission.
*/
void flatcc_builder_set_vtable_clustering(flatcc_builder_t *B, int enable);
/**
* Sets a new user supplied refmap which maps source pointers to
* references and returns the old refmap, or null. It is also
* possible to disable an existing refmap by setting a null
* refmap.
*
* A clone or pick operation may use this map when present,
* depending on the data type. If a hit is found, the stored
* reference will be used instead of performing a new clone or
* pick operation. It is also possible to manually populate the
* refmap. Note that the builder does not have a concept of
* clone or pick - these are higher level recursive operations
* to add data from one buffer to another - but such code may
* rely on the builder to provide the current refmap during
* recursive operations. For this reason, the builder makes no
* calls to the refmap interface on its own - it just stores the
* current refmap such that recursive operations can find it.
*
* Refmaps MUST be reset, replaced or disabled if a source
* pointer may be reused for different purposes - for example if
* repeatedly reading FlatBuffers into the same memory buffer
* and performing a clone into a buffer under construction.
* Refmaps may also be replaced if the same object is to be
* cloned several times keeping the internal DAG structure
* intact with every new clone being an independent object.
*
* Refmaps must also be replaced or disabled prior to starting a
* nested buffer and after stopping it, or when cloning a object
* as a nested root. THIS IS VERY EASY TO GET WRONG! The
* builder does a lot of bookkeeping for nested buffers but not
* in this case. Shared references may happen and they WILL fail
* verification and they WILL break when copying out a nested
* buffer to somewhere else. The user_frame stack may be used
* for pushing refmaps, but often user codes recursive stack
* will work just as well.
*
* It is entirely optional to use refmaps when cloning - they
* preserve DAG structure and may speed up operations or slow
* them down, depending on the source material.
*
* Refmaps may consume a lot of space when large offset vectors
* are cloned when these do not have significant shared
* references. They may also be very cheap to use without any
* dynamic allocation when objects are small and have at most a
* few references.
*
* Refmaps only support init, insert, find, reset, clear but not
* delete. There is a standard implementation in the runtime
* source tree but it can easily be replaced compile time and it
* may also be left out if unused. The builder wraps reset, insert,
* and find so the user does not have to check if a refmap is
* present but other operations must be done direcly on the
* refmap.
*
* The builder wrapped refmap operations are valid on a null
* refmap which will find nothing and insert nothing.
*
* The builder will reset the refmap during a builder reset and
* clear the refmap during a builder clear operation. If the
* refmap goes out of scope before that happens it is important
* to call set_refmap with null and manually clear the refmap.
*/
static inline flatcc_refmap_t *flatcc_builder_set_refmap(flatcc_builder_t *B, flatcc_refmap_t *refmap)
{
flatcc_refmap_t *refmap_old;
refmap_old = B->refmap;
B->refmap = refmap;
return refmap_old;
}
/* Retrieves the current refmap, or null. */
static inline flatcc_refmap_t *flatcc_builder_get_refmap(flatcc_builder_t *B)
{
return B->refmap;
}
/* Finds a reference, or a null reference if no refmap is active. * */
static inline flatcc_builder_ref_t flatcc_builder_refmap_find(flatcc_builder_t *B, const void *src)
{
return B->refmap ? flatcc_refmap_find(B->refmap, src) : flatcc_refmap_not_found;
}
/*
* Inserts into the current refmap with the inseted ref upon
* upon success, or not_found on failure (default 0), or just
* returns ref if refmap is absent.
*
* Note that if an existing item exists, the ref is replaced
* and the new, not the old, ref is returned.
*/
static inline flatcc_builder_ref_t flatcc_builder_refmap_insert(flatcc_builder_t *B, const void *src, flatcc_builder_ref_t ref)
{
return B->refmap ? flatcc_refmap_insert(B->refmap, src, ref) : ref;
}
static inline void flatcc_builder_refmap_reset(flatcc_builder_t *B)
{
if (B->refmap) flatcc_refmap_reset(B->refmap);
}
typedef uint16_t flatcc_builder_buffer_flags_t;
static const flatcc_builder_buffer_flags_t flatcc_builder_is_nested = 1;
static const flatcc_builder_buffer_flags_t flatcc_builder_with_size = 2;
/* The flag size in the API needs to match the internal size. */
static_assert(sizeof(flatcc_builder_buffer_flags_t) ==
sizeof(((flatcc_builder_t *)0)->buffer_flags), "flag size mismatch");
/**
* An alternative to start buffer, start struct/table ... end buffer.
*
* This call is mostly of interest as a means to quicly create a zero
* allocation top-level buffer header following a call to create_struct,
* or to create_vtable/create_table. For that, it is quite simple to
* use. For general buffer construction without allocation, more care is
* needed, as discussed below.
*
* If the content is created with `start/end_table` calls, or similar,
* it is better to use `start/end_buffer` since stack allocation is used
* anyway.
*
* The buffer alignment must be provided manually as it is not derived
* from constructed content, unlike `start/end_buffer`. Typically
* `align` would be same argument as provided to `create_struct`.
* `get_buffer_alignment` may also used (note: `get_buffer_alignment`
* may return different after the call because it will be updated with
* the `block_align` argument to `create_buffer` but that is ok).
*
* The buffer may be constructed as a nested buffer with the `is_nested
* = 1` flag. As a nested buffer a ubyte vector header is placed before
* the aligned buffer header. A top-level buffer will normally have
* flags set to 0.
*
* A top-level buffer may also be constructed with the `with_size = 2`
* flag for top level buffers. It adds a size prefix similar to
* `is_nested` but the size is part of the aligned buffer. A size
* prefixed top level buffer must be accessed with a size prefix aware
* reader, or the buffer given to a standard reader must point to after
* the size field while keeping the buffer aligned to the size field
* (this will depend on the readers API which may be an arbitrary other
* language).
*
* If the `with_size` is used with the `is_nested` flag, the size is
* added as usual and all fields remain aligned as before, but padding
* is adjusted to ensure the buffer is aligned to the size field so
* that, for example, the nested buffer with size can safely be copied
* to a new memory buffer for consumption.
*
* Generally, references may only be used within the same buffer
* context. With `create_buffer` this becomes less precise. The rule
* here is that anything that would be valid with start/end_buffer
* nestings is also valid when removing the `start_buffer` call and
* replacing `end_buffer` with `create_buffer`.
*
* Note the additional burden of tracking buffer alignment manually -
* To help with this use `push_buffer_alignment` where `start_buffer`
* would have been placed, and `pop_buffer_alignment after the
* `create_buffer` call, and use `get_buffer_alignemnt` as described
* above.
*
* `create_buffer` is not suitable as a container for buffers created
* with `start/end_buffer` as these make assumptions about context that
* create buffer does not provide. Also, there is no point in doing so,
* since the idea of `create_buffer` is to avoid allocation in the first
* place.
*/
flatcc_builder_ref_t flatcc_builder_create_buffer(flatcc_builder_t *B,
const char identifier[FLATBUFFERS_IDENTIFIER_SIZE],
uint16_t block_align,
flatcc_builder_ref_t ref, uint16_t align, flatcc_builder_buffer_flags_t flags);
/**
* Creates a struct within the current buffer without using any
* allocation.
*
* The struct should be used as a root in the `end_buffer` call or as a
* union value as there are no other ways to use struct while conforming
* to the FlatBuffer format - noting that tables embed structs in their
* own data area except in union fields.
*
* The struct should be in little endian format and follow the usual
* FlatBuffers alignment rules, although this API won't care about what
* is being stored.
*
* May also be used to simply emit a struct through the emitter
* interface without being in a buffer and without being a valid
* FlatBuffer.
*/
flatcc_builder_ref_t flatcc_builder_create_struct(flatcc_builder_t *B,
const void *data, size_t size, uint16_t align);
/**
* Starts a struct and returns a pointer that should be used immediately
* to fill in the struct in protocol endian format, and when done,
* `end_struct` should be called. The returned reference should be used
* as argument to `end_buffer` or as a union value. See also
* `create_struct`.
*/
void *flatcc_builder_start_struct(flatcc_builder_t *B,
size_t size, uint16_t align);
/**
* Return a pointer also returned at start struct, e.g. for endian
* conversion.
*/
void *flatcc_builder_struct_edit(flatcc_builder_t *B);
/**
* Emits the struct started by `start_struct` and returns a reference to
* be used as root in an enclosing `end_buffer` call or as a union
* value. As mentioned in `create_struct`, these can also be used more
* freely, but not while being conformant FlatBuffers.
*/
flatcc_builder_ref_t flatcc_builder_end_struct(flatcc_builder_t *B);
/**
* The buffer always aligns to at least the offset size (typically 4)
* and the internal alignment requirements of the buffer content which
* is derived as content is added.
*
* In addition, block_align can be specified. This ensures the resulting
* buffer is at least aligned to the block size and that the total size
* is zero padded to fill a block multiple if necessary. Because the
* emitter operates on a virtual address range before the full buffer is
* aligned, it may have to make assumptions based on that: For example,
* it may be processing encryption blocks in the fly, and the resulting
* buffer should be aligned to the encryption block size, even if the
* content is just a byte aligned struct. Block align helps ensure this.
* If the block align as 1 there will be no attempt to zero pad at the
* end, but the content may still warrant padding after the header. End
* padding is only needed with clustered vtables (which is the default).
*
* `block_align` is allowed to be 0 meaning it will inherit from parent if
* present, and otherwise it defaults to 1.
*
* The identifier may be null, and it may optionally be set later with
* `set_identifier` before the `end_buffer` call.
*
* General note:
*
* Only references returned with this buffer as current (i.e. last
* unended buffer) can be stored in other objects (tables, offset
* vectors) also belonging to this buffer, or used as the root argument
* to `end_buffer`. A reference may be stored at most once, and unused
* references will result in buffer garbage. All calls must be balanced
* around the respective start / end operations, but may otherwise nest
* freely, including nested buffers. Nested buffers are supposed to be
* stored in a table offset field to comply with FlatBuffers, but the
* API does not place any restrictions on where references are stored,
* as long as they are indicated as offset fields.
*
* All alignment in all API calls must be between 1 and 256 and must be a
* power of 2. This is not checked. Only if explicitly documented can it
* also be 0 for a default value.
*
* `flags` can be `with_size` but `is_nested` is derived from context
* see also `create_buffer`.
*/
int flatcc_builder_start_buffer(flatcc_builder_t *B,
const char identifier[FLATBUFFERS_IDENTIFIER_SIZE],
uint16_t block_align, flatcc_builder_buffer_flags_t flags);
/**
* The root object should be a struct or a table to conform to the
* FlatBuffers format, but technically it can also be a vector or a
* string, or even a child buffer (which is also vector as seen by the
* buffer). The object must be created within the current buffer
* context, that is, while the current buffer is the deepest nested
* buffer on the stack.
*/
flatcc_builder_ref_t flatcc_builder_end_buffer(flatcc_builder_t *B, flatcc_builder_ref_t root);
/**
* The embed buffer is mostly intended to add an existing buffer as a
* nested buffer. The buffer will be wrapped in a ubyte vector such that
* the buffer is aligned at vector start, after the size field.
*
* If `align` is 0 it will default to 8 so that all FlatBuffer numeric
* types will be readable. NOTE: generally do not count on align 0 being
* valid or even checked by the API, but in this case it may be
* difficult to know the internal buffer alignment, and 1 would be the wrong
* choice.
*
* If `block_align` is set (non-zero), the buffer is placed in an isolated
* block multiple. This may cost up to almost 2 block sizes in padding.
* If the `block_align` argument is 0, it inherits from the parent
* buffer block_size, or defaults to 1.
*
* The `align` argument must be set to respect the buffers internal
* alignment requirements, but if the buffer is smaller it will not be
* padded to isolate the buffer. For example a buffer of with
* `align = 64` and `size = 65` may share its last 64 byte block with
* other content, but not if `block_align = 64`.
*
* Because the ubyte size field is not, by default, part of the aligned
* buffer, significant space can be wasted if multiple blocks are added
* in sequence with a large block size.
*
* In most cases the distinction between the two alignments is not
* important, but it allows separate configuration of block internal
* alignment and block size, which can be important for auto-generated
* code that may know the alignment of the buffer, but not the users
* operational requirements.
*
* If the buffer is embedded without a parent buffer, it will simply
* emit the buffer through the emit interface, but may also add padding
* up to block alignment. At top-level there will be no size field
* header.
*
* If `with_size` flag is set, the buffer is aligned to size field and
* the above note about padding space no longer applies. The size field
* is added regardless. The `is_nested` flag has no effect since it is
* impplied.
*/
flatcc_builder_ref_t flatcc_builder_embed_buffer(flatcc_builder_t *B,
uint16_t block_align,
const void *data, size_t size, uint16_t align, flatcc_builder_buffer_flags_t flags);
/**
* Applies to the innermost open buffer. The identifier may be null or
* contain all zero. Overrides any identifier given to the start buffer
* call.
*/
void flatcc_builder_set_identifier(flatcc_builder_t *B,
const char identifier[FLATBUFFERS_IDENTIFIER_SIZE]);
enum flatcc_builder_type {
flatcc_builder_empty = 0,
flatcc_builder_buffer,
flatcc_builder_struct,
flatcc_builder_table,
flatcc_builder_vector,
flatcc_builder_offset_vector,
flatcc_builder_string,
flatcc_builder_union_vector
};
/**
* Returns the object type currently on the stack, for example if
* needing to decide how to close a buffer. Because a table is
* automatically added when starting a table buffer,
* `flatcc_builder_table_buffer` should not normally be seen and the level
* should be 2 before when closing a top-level table buffer, and 0
* after. A `flatcc_builder_struct_buffer` will be visible at level 1.
*
*/
enum flatcc_builder_type flatcc_builder_get_type(flatcc_builder_t *B);
/**
* Similar to `get_type` but for a specific level. `get_type_at(B, 1)`
* will return `flatcc_builder_table_buffer` if this is the root buffer
* type. get_type_at(B, 0) is always `flatcc_builder_empty` and so are any
* level above `get_level`.
*/
enum flatcc_builder_type flatcc_builder_get_type_at(flatcc_builder_t *B, int level);
/**
* The user stack is available for custom data. It may be used as
* a simple stack by extending or reducing the inner-most frame.
*
* A frame has a size and a location on the user stack. Entering
* a frame ensures the start is aligned to sizeof(size_t) and
* ensures the requested space is available without reallocation.
* When exiting a frame, the previous frame is restored.
*
* A user frame works completely independently of the builders
* frame stack for tracking tables vectors etc. and does not have
* to be completely at exit, but obviously it is not valid to
* exit more often the entered.
*
* The frame is zeroed when entered.
*
* Returns a non-zero handle to the user frame upon success or
* 0 on allocation failure.
*/
size_t flatcc_builder_enter_user_frame(flatcc_builder_t *B, size_t size);
/**
* Makes the parent user frame current, if any. It is not valid to call
* if there isn't any current frame. Returns handle to parent frame if
* any, or 0.
*/
size_t flatcc_builder_exit_user_frame(flatcc_builder_t *B);
/**
* Exits the frame represented by the given handle. All more
* recently entered frames will also be exited. Returns the parent
* frame handle if any, or 0.