#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 #ifndef UINT8_MAX #include #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. */ size_t flatcc_builder_exit_user_frame_at(flatcc_builder_t *B, size_t handle); /** * Returns a non-zero handle to the current inner-most user frame if * any, or 0. */ size_t flatcc_builder_get_current_user_frame(flatcc_builder_t *B); /* * Returns a pointer to the user frame at the given handle. Any active * frame can be accessed in this manner but the pointer is invalidated * by user frame enter and exit operations. */ void *flatcc_builder_get_user_frame_ptr(flatcc_builder_t *B, size_t handle); /** * Returns the size of the buffer and the logical start and end address * of with respect to the emitters address range. `end` - `start` also * yields the size. During construction `size` is the emitted number of * bytes and after buffer close it is the actual buffer size - by then * the start is also the return value of close buffer. End marks the end * of the virtual table cluster block. * * NOTE: there is no guarantee that all vtables end up in the cluster * block if there is placed a limit on the vtable size, or if nested * buffers are being used. On the other hand, if these conditions are * met, it is guaranteed that all vtables are present if the vtable * block is available (this depends on external transmission - the * vtables are always emitted before tables using them). In all cases * the vtables will behave as valid vtables in a flatbuffer. */ size_t flatcc_builder_get_buffer_size(flatcc_builder_t *B); /** * Returns the reference to the start of the emitter buffer so far, or * in total after buffer end, in the virtual address range used * by the emitter. Start is also returned by buffer end. */ flatcc_builder_ref_t flatcc_builder_get_buffer_start(flatcc_builder_t *B); /** * Returns the reference to the end of buffer emitted so far. When * clustering vtables, this is the end of tables, or after buffer end, * also zero padding if block aligned. If clustering is disabled, this * method will return 0 as the buffer only grows down then. */ flatcc_builder_ref_t flatcc_builder_get_buffer_mark(flatcc_builder_t *B); /** * Creates the vtable in the current buffer context, somewhat similar to * how create_vector operates. Each call results in a new table even if * an identical has already been emitted. * * Also consider `create_cached_vtable` which will reuse existing * vtables. * * This is low-low-level function intended to support * `create_cached_vtable` or equivalent, and `create_table`, both of * which are normally used indirectly via `start_table`, `table_add`, * `table_add_offset`..., `table_end`. * * Creates a vtable as a verbatim copy. This means the vtable must * include the header fields containing the vtable size and the table * size in little endian voffset_t encoding followed by the vtable * entries in same encoding. * * The function may be used to copy vtables from other other buffers * since they are directly transferable. * * The returned reference is actually the emitted location + 1. This * ensures the vtable is not mistaken for error because 0 is a valid * vtable reference. `create_table` is aware of this and substracts one * before computing the final offset relative to the table. This also * means vtable references are uniquely identifiable by having the * lowest bit set. * * vtable references may be reused within the same buffer, not any * parent or other related buffer (technically this is possible though, * as long as it is within same builder context, but it will not construct * valid FlatBuffers because the buffer cannot be extracted in isolation). */ flatcc_builder_vt_ref_t flatcc_builder_create_vtable(flatcc_builder_t *B, const flatbuffers_voffset_t *vt, flatbuffers_voffset_t vt_size); /** * Support function to `create_vtable`. See also the uncached version * `create_vtable`. * * Looks up the constructed vtable on the vs stack too see if it matches * a cached entry. If not, it emits a new vtable either at the end if * top-level and clustering is enabled, or at the front (always for * nested buffers). * * If the same vtable was already emitted in a different buffer, but not * in the current buffer, the cache entry will be reused, but a new * table will be emitted the first it happens in the same table. * * The returned reference is + 1 relative to the emitted address range * to identify it as a vtable and to avoid mistaking the valid 0 * reference for an error (clustered vtables tend to start at the end at * the virtual address 0, and up). * * The hash function can be chosen arbitrarily but may result in * duplicate emitted vtables if different hash functions are being used * concurrently, such as mixing the default used by `start/end table` * with a custom function (this is not incorrect, it only increases the * buffer size and cache pressure). * * If a vtable has a unique ID by other means than hashing the content, * such as an integer id, and offset into another buffer, or a pointer, * a good hash may be multiplication by a 32-bit prime number. The hash * table is not very sensitive to collissions as it uses externally * chained hashing with move to front semantics. */ flatcc_builder_vt_ref_t flatcc_builder_create_cached_vtable(flatcc_builder_t *B, const flatbuffers_voffset_t *vt, flatbuffers_voffset_t vt_size, uint32_t vt_hash); /* * Based on Knuth's prime multiplier. * * This is an incremental hash that is called with id and size of each * non-empty field, and finally with the two vtable header fields * when vtables are constructed via `table_add/table_add_offset`. * */ #ifndef FLATCC_SLOW_MUL #ifndef FLATCC_BUILDER_INIT_VT_HASH #define FLATCC_BUILDER_INIT_VT_HASH(hash) { (hash) = (uint32_t)0x2f693b52UL; } #endif #ifndef FLATCC_BUILDER_UPDATE_VT_HASH #define FLATCC_BUILDER_UPDATE_VT_HASH(hash, id, offset) \ { (hash) = (((((uint32_t)id ^ (hash)) * (uint32_t)2654435761UL)\ ^ (uint32_t)(offset)) * (uint32_t)2654435761UL); } #endif #ifndef FLATCC_BUILDER_BUCKET_VT_HASH #define FLATCC_BUILDER_BUCKET_VT_HASH(hash, width) (((uint32_t)(hash)) >> (32 - (width))) #endif #endif /* * By default we use Bernsteins hash as fallback if multiplication is slow. * * This just have to be simple, fast, and work on devices without fast * multiplication. We are not too sensitive to collisions. Feel free to * experiment and replace. */ #ifndef FLATCC_BUILDER_INIT_VT_HASH #define FLATCC_BUILDER_INIT_VT_HASH(hash) { (hash) = 5381; } #endif #ifndef FLATCC_BUILDER_UPDATE_VT_HASH #define FLATCC_BUILDER_UPDATE_VT_HASH(hash, id, offset) \ { (hash) = ((((hash) << 5) ^ (id)) << 5) ^ (offset); } #endif #ifndef FLATCC_BUILDER_BUCKET_VT_HASH #define FLATCC_BUILDER_BUCKET_VT_HASH(hash, width) (((1 << (width)) - 1) & (hash)) #endif /** * Normally use `start_table` instead of this call. * * This is a low-level call only intended for high-performance * applications that repeatedly churn about similar tables of known * layout, or as a support layer for other builders that maintain their * own allocation rather than using the stack of this builder. * * Creates a table from an already emitted vtable, actual data that is * properly aligned relative to data start and in little endian * encoding. Unlike structs, tables can have offset fields. These must * be stored as flatcc_builder_ref_t types (which have uoffset_t size) as * returned by the api in native encoding. The `offsets` table contain * voffsets relative to `data` start (this is different from how vtables * store offsets because they are relative to a table header). The * `offsets` table is only used temporarily to translate the stored * references and is not part of final buffer content. `offsets` may be * null if `offset_count` is 0. `align` should be the highest aligned * field in the table, but `size` need not be a multiple of `align`. * Aside from endian encoding, the vtable must record a table size equal * to `size + sizeof(flatbuffers_uoffset_t)` because it includes the * table header field size. The vtable is not accessed by this call (nor * is it available). Unlike other references, the vtable reference may * be shared between tables in the same buffer (not with any related * buffer such as a parent buffer). * * The operation will not use any allocation, but will update the * alignment of the containing buffer if any. * * Note: unlike other create calls, except `create_offset_vector`, * the source data is modified in order to translate references intok * offsets before emitting the table. */ flatcc_builder_ref_t flatcc_builder_create_table(flatcc_builder_t *B, const void *data, size_t size, uint16_t align, flatbuffers_voffset_t *offsets, int offset_count, flatcc_builder_vt_ref_t vt_ref); /** * Starts a table, typically following a start_buffer call as an * alternative to starting a struct, or to create table fields to be * stored in a parent table, or in an offset vector. * A number of `table_add` and table_add_offset` call may be placed * before the `end_table` call. Struct fields should NOT use `struct` * related call (because table structs are in-place), rather they should * use the `table_add` call with the appropriate size and alignment. * * A table, like other reference returning calls, may also be started * outside a buffer if the buffer header and alignment is of no * interest to the application, for example as part of an externally * built buffer. * * `count` must be larger than the largest id used for this table * instance. Normally it is set to the number of fields defined in the * schema, but it may be less if memory is constrained and only few * fields with low valued id's are in use. The count can extended later * with `reserve_table` if necessary. `count` may be also be set to a * large enough value such as FLATBUFFERS_ID_MAX + 1 if memory is not a * concern (reserves about twice the maximum vtable size to track the * current vtable and voffsets where references must be translated to * offsets at table end). `count` may be zero if for example * `reserve_table` is being used. * * Returns -1 on error, 0 on success. */ int flatcc_builder_start_table(flatcc_builder_t *B, int count); /** * Call before adding a field with an id that is not below the count set * at table start. Not needed in most cases. For performance reasons * the builder does not check all bounds all the the time, but the user * can do so if memory constraints prevent start_table from using a * conservative value. See also `table_start`. * * Note: this call has absolutely no effect on the table layout, it just * prevents internal buffer overruns. * * Returns -1 on error, 0 on success. */ int flatcc_builder_reserve_table(flatcc_builder_t *B, int count); /** * Completes the table constructed on the internal stack including * emitting a vtable, or finding a matching vtable that has already been * emitted to the same buffer. (Vtables cannot be shared between * buffers, but they can between tables of the same buffer). * * Note: there is a considerable, but necessary, amount of bookkeeping * involved in constructing tables. The `create_table` call is much * faster, but it also expects a lot of work to be done already. * * Tables can be created with no fields added. This will result in an * empty vtable and a table with just a vtable reference. If a table is * used as a sub-table, such a table might also not be stored at all, * but we do not return a special reference for that, nor do we provide * and option to not create the table in this case. This may be * interpreted as the difference between a null table (not stored in * parent), and an empty table with a unique offset (and thus identity) * different from other empty tables. */ flatcc_builder_ref_t flatcc_builder_end_table(flatcc_builder_t *B); /** * Optionally this method can be called just before `flatcc_builder_end_table` * to verify that all required fields have been set. * Each entry is a table field id. * * Union fields should use the type field when checking for presence and * may also want to check the soundness of the union field overall using * `check_union_field` with the id one higher than the type field id. * * This funcion is typically called by an assertion in generated builder * interfaces while release builds may want to avoid this performance * overhead. * * Returns 1 if all fields are matched, 0 otherwise. */ int flatcc_builder_check_required(flatcc_builder_t *B, const flatbuffers_voffset_t *required, int count); /** * Same as `check_required` when called with a single element. * * Typically used when direct calls are more convenient than building an * array first. Useful when dealing with untrusted intput such as parsed * text from an external source. */ int flatcc_builder_check_required_field(flatcc_builder_t *B, flatbuffers_voffset_t id); /** * Checks that a union field is valid. * * The criteria is: * * If the type field is not present (at id - 1), or it holds a zero value, * then the table field (at id) must be present. * * Generated builder code may be able to enforce valid unions without * this check by setting both type and table together, but e.g. parsers * may receive the type and the table independently and then it makes * sense to validate the union fields before table completion. * * Note that an absent union field is perfectly valid. If a union is * required, the type field (id - 1), should be checked separately * while the table field should only be checked here because it can * (and must) be absent when the type is NONE (= 0). */ int flatcc_builder_check_union_field(flatcc_builder_t *B, flatbuffers_voffset_t id); /** * A struct, enum or scalar added should be stored in little endian in * the return pointer location. The pointer is short lived and will * not necessarily survive other builder calls. * * A union type field can also be set using this call. In fact, this is * the only way to deal with unions via this API. Consequently, it is * the users repsonsibility to ensure the appropriate type is added * at the next higher id. * * Null and default values: * * FlatBuffers does not officially provide an option for null values * because it does not distinguish between default values and values * that are not present. At this api level, we do not deal with defaults * at all. Callee should test the stored value against the default value * and only add the field if it does not match the default. This only * applies to scalar and enum values. Structs cannot have defaults so * their absence means null, and strings, vectors and subtables do have * natural null values different from the empty object and empty objects * with different identity is also possible. * * To handle Null for scalars, the following approach is recommended: * * Provide a schema-specific `add` operation that only calls this * low-level add method if the default does not match, and also provide * another `set` operation that always stores the value, regardless of * default. For most readers this will be transparent, except for extra * space used, but for Null aware readers, these can support operations * to test for Null/default/other value while still supporting the * normal read operation that returns default when a value is absent * (i.e. Null). * * It is valid to call with a size of 0 - the effect being adding the * vtable entry. The call may also be dropped in this case to reduce * the vtable size - the difference will be in null detection. */ void *flatcc_builder_table_add(flatcc_builder_t *B, int id, size_t size, uint16_t align); /** * Returns a pointer to the buffer holding the last field added. The * size argument must match the field size added. May, for example, be * used to perform endian conversion after initially updating field * as a native struct. Must be called before the table is ended. */ void *flatcc_builder_table_edit(flatcc_builder_t *B, size_t size); /** * Similar to `table_add` but copies source data into the buffer before * it is returned. Useful when adding a larger struct already encoded in * little endian. */ void *flatcc_builder_table_add_copy(flatcc_builder_t *B, int id, const void *data, size_t size, uint16_t align); /** * Add a string, vector, or sub-table depending on the type if the * field identifier. The offset ref obtained when the field object was * closed should be stored as is in the given pointer. The pointer * is only valid short term, so create the object before calling * add to table, but the owner table can be started earlier. Never mix * refs from nested buffers with parent buffers. * * Also uses this method to add nested buffers. A nested buffer is * simple a buffer created while another buffer is open. The buffer * close operation provides the necessary reference. * * When the table closes, all references get converted into offsets. * Before that point, it is not required that the offset is written * to. */ flatcc_builder_ref_t *flatcc_builder_table_add_offset(flatcc_builder_t *B, int id); /* * Adds a union type and reference in a single operation and returns 0 * on success. Stores the type field at `id - 1` and the value at * `id`. The `value` is a reference to a table, to a string, or to a * standalone `struct` outside the table. * * If the type is 0, the value field must also be 0. * * Unions can also be added as separate calls to the type and the offset * separately which can lead to better packing when the type is placed * together will other small fields. */ int flatcc_builder_table_add_union(flatcc_builder_t *B, int id, flatcc_builder_union_ref_t uref); /* * Adds a union type vector and value vector in a single operations * and returns 0 on success. * * If both the type and value vector is null, nothing is added. * Otherwise both must be present and have the same length. * * Any 0 entry in the type vector must also have a 0 entry in * the value vector. */ int flatcc_builder_table_add_union_vector(flatcc_builder_t *B, int id, flatcc_builder_union_vec_ref_t uvref); /** * Creates a vector in a single operation using an externally supplied * buffer. This completely bypasses the stack, but the size must be * known and the content must be little endian. Do not use for strings * and offset vectors. Other flatbuffer vectors could be used as a * source, but the length prefix is not required. * * Set `max_count` to `FLATBUFFERS_COUNT_MAX(elem_size)` before a call * to any string or vector operation to the get maximum safe vector * size, or use (size_t)-1 if overflow is not a concern. * * The max count property is a global property that remains until * explicitly changed. * * `max_count` is to prevent malicous or accidental overflow which is * difficult to detect by multiplication alone, depending on the type * sizes being used and having `max_count` thus avoids a division for * every vector created. `max_count` does not guarantee a vector will * fit in an empty buffer, it just ensures the internal size checks do * not overflow. A safe, sane limit woud be max_count / 4 because that * is half the maximum buffer size that can realistically be * constructed, corresponding to a vector size of `UOFFSET_MAX / 4` * which can always hold the vector in 1GB excluding the size field when * sizeof(uoffset_t) = 4. */ flatcc_builder_ref_t flatcc_builder_create_vector(flatcc_builder_t *B, const void *data, size_t count, size_t elem_size, uint16_t align, size_t max_count); /** * Starts a vector on the stack. * * Do not use these calls for string or offset vectors, but do store * scalars, enums and structs, always in little endian encoding. * * Use `extend_vector` subsequently to add zero, one or more elements * at time. * * See `create_vector` for `max_count` argument (strings and offset * vectors have a fixed element size and does not need this argument). * * Returns 0 on success. */ int flatcc_builder_start_vector(flatcc_builder_t *B, size_t elem_size, uint16_t align, size_t max_count); /** * Emits the vector constructed on the stack by start_vector. * * The vector may be accessed in the emitted stream using the returned * reference, even if the containing buffer is still under construction. * This may be useful for sorting. This api does not support sorting * because offset vectors cannot read their references after emission, * and while plain vectors could be sorted, it has been chosen that this * task is better left as a separate processing step. Generated code can * provide sorting functions that work on final in-memory buffers. */ flatcc_builder_ref_t flatcc_builder_end_vector(flatcc_builder_t *B); /** Returns the number of elements currently on the stack. */ size_t flatcc_builder_vector_count(flatcc_builder_t *B); /** * Returns a pointer ot the first vector element on stack, * accessible up to the number of elements currently on stack. */ void *flatcc_builder_vector_edit(flatcc_builder_t *B); /** * Returns a zero initialized buffer to a new region of the vector which * is extended at the end. The buffer must be consumed before other api * calls that may affect the stack, including `extend_vector`. * * Do not use for strings, offset or union vectors. May be used for nested * buffers, but these have dedicated calls to provide better alignment. */ void *flatcc_builder_extend_vector(flatcc_builder_t *B, size_t count); /** * A specialized `vector_extend` that pushes a single element. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. Note: for structs, care must be taken to ensure * the source has been zero padded. For this reason it may be better to * use extend(B, 1) and assign specific fields instead. */ void *flatcc_builder_vector_push(flatcc_builder_t *B, const void *data); /** * Pushes multiple elements at a time. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. */ void *flatcc_builder_append_vector(flatcc_builder_t *B, const void *data, size_t count); /** * Removes elements already added to vector that has not been ended. * For example, a vector of parsed list may remove the trailing comma, * or the vector may simply overallocate to get some temporary working * space. The total vector size must never become negative. * * Returns -1 if the count as larger than current count, or 0 on success. */ int flatcc_builder_truncate_vector(flatcc_builder_t *B, size_t count); /* * Similar to `create_vector` but with references that get translated * into offsets. The references must, as usual, belong to the current * buffer. Strings, scalar and struct vectors can emit directly without * stack allocation, but offset vectors must translate the offsets * and therefore need the temporary space. Thus, this function is * roughly equivalent to to start, append, end offset vector. * * See also `flatcc_builder_create_offset_vector_direct`. */ flatcc_builder_ref_t flatcc_builder_create_offset_vector(flatcc_builder_t *B, const flatcc_builder_ref_t *data, size_t count); /* * NOTE: this call takes non-const source array of references * and destroys the content. * * This is a faster version of `create_offset_vector` where the * source references are destroyed. In return the vector can be * emitted directly without passing over the stack. */ flatcc_builder_ref_t flatcc_builder_create_offset_vector_direct(flatcc_builder_t *B, flatcc_builder_ref_t *data, size_t count); /** * Starts a vector holding offsets to tables or strings. Before * completion it will hold `flatcc_builder_ref_t` references because the * offset is not known until the vector start location is known, which * depends to the final size, which for parsers is generally unknown. */ int flatcc_builder_start_offset_vector(flatcc_builder_t *B); /** * Similar to `end_vector` but updates all stored references so they * become offsets to the vector start. */ flatcc_builder_ref_t flatcc_builder_end_offset_vector(flatcc_builder_t *B); /** * Same as `flatcc_builder_end_offset_vector` except null references are * permitted when the corresponding `type` entry is 0 (the 'NONE' type). * This makes it possible to build union vectors with less overhead when * the `type` vector is already known. Use standand offset vector calls * prior to this call. */ flatcc_builder_ref_t flatcc_builder_end_offset_vector_for_unions(flatcc_builder_t *B, const flatcc_builder_utype_t *type); /** Returns the number of elements currently on the stack. */ size_t flatcc_builder_offset_vector_count(flatcc_builder_t *B); /** * Returns a pointer ot the first vector element on stack, * accessible up to the number of elements currently on stack. */ void *flatcc_builder_offset_vector_edit(flatcc_builder_t *B); /** * Similar to `extend_vector` but returns a buffer indexable as * `flatcc_builder_ref_t` array. All elements must be set to a valid * unique non-null reference, but truncate and extend may be used to * perform edits. Unused references will leave garbage in the buffer. * References should not originate from any other buffer than the * current, including parents and nested buffers. It is valid to reuse * references in DAG form when contained in the sammer, excluding any * nested, sibling or parent buffers. */ flatcc_builder_ref_t *flatcc_builder_extend_offset_vector(flatcc_builder_t *B, size_t count); /** Similar to truncate_vector. */ int flatcc_builder_truncate_offset_vector(flatcc_builder_t *B, size_t count); /** * A specialized extend that pushes a single element. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. */ flatcc_builder_ref_t *flatcc_builder_offset_vector_push(flatcc_builder_t *B, flatcc_builder_ref_t ref); /** * Takes an array of refs as argument to do a multi push operation. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. */ flatcc_builder_ref_t *flatcc_builder_append_offset_vector(flatcc_builder_t *B, const flatcc_builder_ref_t *refs, size_t count); /** * All union vector operations are like offset vector operations, * except they take a struct with a type and a reference rather than * just a reference. The finished union vector is returned as a struct * of two references, one for the type vector and one for the table offset * vector. Each reference goes to a separate table field where the type * offset vector id must be one larger than the type vector. */ /** * Creates a union vector which is in reality two vectors, a type vector * and an offset vector. Both vectors references are returned. */ flatcc_builder_union_vec_ref_t flatcc_builder_create_union_vector(flatcc_builder_t *B, const flatcc_builder_union_ref_t *urefs, size_t count); /* * NOTE: this call takes non-const source array of references * and destroys the content. The type array remains intact. * * This is a faster version of `create_union_vector` where the source * references are destroyed and where the types are given in a separate * array. In return the vector can be emitted directly without passing * over the stack. * * Unlike `create_offset_vector` we do allow null references but only if * the union type is NONE (0). */ flatcc_builder_union_vec_ref_t flatcc_builder_create_union_vector_direct(flatcc_builder_t *B, const flatcc_builder_utype_t *types, flatcc_builder_ref_t *data, size_t count); /* * Creates just the type vector part of a union vector. This is * similar to a normal `create_vector` call except that the size * and alignment are given implicitly. Can be used during * cloning or similar operations where the types are all given * but the values must be handled one by one as prescribed by * the type. The values can be added separately as an offset vector. */ flatcc_builder_ref_t flatcc_builder_create_type_vector(flatcc_builder_t *B, const flatcc_builder_utype_t *types, size_t count); /** * Starts a vector holding types and offsets to tables or strings. Before * completion it will hold `flatcc_builder_union_ref_t` references because the * offset is not known until the vector start location is known, which * depends to the final size, which for parsers is generally unknown, * and also because the union type must be separated out into a separate * vector. It would not be practicaly to push on two different vectors * during construction. */ int flatcc_builder_start_union_vector(flatcc_builder_t *B); /** * Similar to `end_vector` but updates all stored references so they * become offsets to the vector start and splits the union references * into a type vector and an offset vector. */ flatcc_builder_union_vec_ref_t flatcc_builder_end_union_vector(flatcc_builder_t *B); /** Returns the number of elements currently on the stack. */ size_t flatcc_builder_union_vector_count(flatcc_builder_t *B); /** * Returns a pointer ot the first vector element on stack, * accessible up to the number of elements currently on stack. */ void *flatcc_builder_union_vector_edit(flatcc_builder_t *B); /** * Similar to `extend_offset_vector` but returns a buffer indexable as a * `flatcc_builder_union_ref_t` array. All elements must be set to a valid * unique non-null reference with a valid union type to match, or it * must be null with a zero union type. */ flatcc_builder_union_ref_t *flatcc_builder_extend_union_vector(flatcc_builder_t *B, size_t count); /** Similar to truncate_vector. */ int flatcc_builder_truncate_union_vector(flatcc_builder_t *B, size_t count); /** * A specialized extend that pushes a single element. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. */ flatcc_builder_union_ref_t *flatcc_builder_union_vector_push(flatcc_builder_t *B, flatcc_builder_union_ref_t uref); /** * Takes an array of union_refs as argument to do a multi push operation. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. */ flatcc_builder_union_ref_t *flatcc_builder_append_union_vector(flatcc_builder_t *B, const flatcc_builder_union_ref_t *urefs, size_t count); /** * Faster string operation that avoids temporary stack storage. The * string is not required to be zero-terminated, but is expected * (unchecked) to be utf-8. Embedded zeroes would be allowed but * ubyte vectors should be used for that. The resulting string will * have a zero termination added, not included in length. */ flatcc_builder_ref_t flatcc_builder_create_string(flatcc_builder_t *B, const char *s, size_t len); /** `create_string` up to zero termination of source. */ flatcc_builder_ref_t flatcc_builder_create_string_str(flatcc_builder_t *B, const char *s); /** * `create_string` up to zero termination or at most max_len of source. * * Note that like `strncpy` it will include `max_len` characters if * the source is longer than `max_len`, but unlike `strncpy` it will * always add zero termination. */ flatcc_builder_ref_t flatcc_builder_create_string_strn(flatcc_builder_t *B, const char *s, size_t max_len); /** * Starts an empty string that can be extended subsequently. * * While the string is being created, it is guaranteed that there is * always a null character after the end of the current string length. * This also holds after `extend` and `append` operations. It is not * allowed to modify the null character. * * Returns 0 on success. */ int flatcc_builder_start_string(flatcc_builder_t *B); /** * Similar to `extend_vector` except for the buffer return type and a * slight speed advantage. Strings are expected to contain utf-8 content * but this isn't verified, and null characters would be accepted. The * length is given in bytes. * * Appending too much, then truncating can be used to trim string * escapes during parsing, or convert between unicode formats etc. */ char *flatcc_builder_extend_string(flatcc_builder_t *B, size_t len); /** * Concatenes a length of string. If the string contains zeroes (which * it formally shouldn't), they will be copied in. * * Returns the buffer holding a modifiable copy of the added content, * or null on error. */ char *flatcc_builder_append_string(flatcc_builder_t *B, const char *s, size_t len); /** `append_string` up to zero termination of source. */ char *flatcc_builder_append_string_str(flatcc_builder_t *B, const char *s); /** `append_string` up zero termination or at most max_len of source. */ char *flatcc_builder_append_string_strn(flatcc_builder_t *B, const char *s, size_t max_len); /** * Similar to `truncate_vector` available for consistency and a slight * speed advantage. Reduces string by `len` bytes - it does not set * the length. The resulting length must not become negative. Zero * termination is not counted. * * Returns -1 of the length becomes negative, 0 on success. */ int flatcc_builder_truncate_string(flatcc_builder_t *B, size_t len); /** * Similar to `end_vector` but adds a trailing zero not included * in the length. The trailing zero is added regardless of whatever * zero content may exist in the provided string (although it * formally should not contain any). */ flatcc_builder_ref_t flatcc_builder_end_string(flatcc_builder_t *B); /** Returns the length of string currently on the stack. */ size_t flatcc_builder_string_len(flatcc_builder_t *B); /** * Returns a ponter to the start of the string * accessible up the length of string currently on the stack. */ char *flatcc_builder_string_edit(flatcc_builder_t *B); /* * Only for use with the default emitter. * * Fast acces to small buffers from default emitter. * * Only valid for default emitters before `flatcc_builder_clear`. The * return buffer is not valid after a call to `flatcc_builder_reset` or * `flatcc_builder_clear`. * * Returns null if the buffer size is too large to a have a linear * memory representation or if the emitter is not the default. A valid * size is between half and a full emitter page size depending on vtable * content. * * Non-default emitters must be accessed by means specific to the * particular emitter. * * If `size_out` is not null, it is set to the buffer size, or 0 if * operation failed. * * The returned buffer should NOT be deallocated explicitly. * * The buffer size is the size reported by `flatcc_builder_get_buffer_size`. */ void *flatcc_builder_get_direct_buffer(flatcc_builder_t *B, size_t *size_out); /* * Only for use with the default emitter. * * Default finalizer that allocates a buffer from the default emitter. * * Returns null if memory could not be allocated or if the emitter is * not the default. This is just a convenience method - there are many * other possible ways to extract the result of the emitter depending on * use case. * * If `size_out` is not null, it is set to the buffer size, or 0 if * operation failed. * * The allocated buffer is aligned according to malloc which may not be * sufficient in advanced cases - for that purpose * `flatcc_builder_finalize_aligned_buffer` may be used. * * It may be worth calling `flatcc_builder_get_direct_buffer` first to see * if the buffer is small enough to avoid copying. * * The returned buffer must be deallocated using `free`. */ void *flatcc_builder_finalize_buffer(flatcc_builder_t *B, size_t *size_out); /* * Only for use with the default emitter. * * Similar to `flatcc_builder_finalize_buffer` but ensures the returned * memory is aligned to the overall alignment required for the buffer. * Often it is not necessary unless special operations rely on larger * alignments than the stored scalars. * * If `size_out` is not null, it is set to the buffer size, or 0 if * operation failed. * * The returned buffer must be deallocated using `aligned_free` which is * implemented via `flatcc_flatbuffers.h`. `free` will usually work but * is not portable to platforms without posix_memalign or C11 * aligned_alloc support. * * NOTE: if a library might be compiled with a version of aligned_free * that differs from the application using it, use * `flatcc_builder_aligned_free` to make sure the correct deallocation * function is used. */ void *flatcc_builder_finalize_aligned_buffer(flatcc_builder_t *B, size_t *size_out); /* * A stable implementation of `aligned_alloc` that is not sensitive * to the applications compile time flags. */ void *flatcc_builder_aligned_alloc(size_t alignment, size_t size); /* * A stable implementation of `aligned_free` that is not sensitive * to the applications compile time flags. */ void flatcc_builder_aligned_free(void *p); /* * Same allocation as `flatcc_builder_finalize_buffer` returnes. Usually * same as `malloc` but can redefined via macros. */ void *flatcc_builder_alloc(size_t size); /* * A stable implementation of `free` when the default allocation * methods have been redefined. * * Deallocates memory returned from `flatcc_builder_finalize_buffer`. */ void flatcc_builder_free(void *p); /* * Only for use with the default emitter. * * Convenience method to copy buffer from default emitter. Forwards * call to default emitter and returns input pointer, or null if * the emitter is not default or of the given size is smaller than * the buffer size. * * Note: the `size` argument is the target buffers capacity, not the * flatcc_builders buffer size. * * Other emitters have custom interfaces for reaching their content. */ void *flatcc_builder_copy_buffer(flatcc_builder_t *B, void *buffer, size_t size); #ifdef __cplusplus } #endif #endif /* FLATCC_BUILDER_H */