Branch data Line data Source code
1 : : #ifndef FLATCC_EMITTER_H
2 : : #define FLATCC_EMITTER_H
3 : :
4 : : /*
5 : : * Default implementation of a flatbuilder emitter.
6 : : *
7 : : * This may be used as a starting point for more advanced emitters,
8 : : * for example writing completed pages to disk or network and
9 : : * the recycling those pages.
10 : : */
11 : :
12 : : #include <stdlib.h>
13 : : #include <string.h>
14 : :
15 : : #include "flatcc/flatcc_types.h"
16 : : #include "flatcc/flatcc_iov.h"
17 : :
18 : : /*
19 : : * The buffer steadily grows during emission but the design allows for
20 : : * an extension where individual pages can recycled before the buffer
21 : : * is complete, for example because they have been transmitted.
22 : : *
23 : : * When done, the buffer can be cleared to free all memory, or reset to
24 : : * maintain an adaptive page pool for next buffer construction.
25 : : *
26 : : * Unlike an exponentially growing buffer, each buffer page remains
27 : : * stable in memory until reset, clear or recycle is called.
28 : : *
29 : : * Design notes for possible extensions:
30 : : *
31 : : * The buffer is a ring buffer marked by a front and a back page. The
32 : : * front and back may be the same page and may initially be absent.
33 : : * Anything outside these pages are unallocated pages for recycling.
34 : : * Any page between (but excluding) the front and back pages may be
35 : : * recycled by unlinking and relinking outside the front and back pages
36 : : * but then copy operations no longer makes sense. Each page stores the
37 : : * logical offset within the buffer but isn't otherwise used by the
38 : : * implemention - it might be used for network transmission. The buffer
39 : : * is not explicitly designed for multithreaded access but any page
40 : : * strictly between front and back is not touched unless recycled and in
41 : : * this case aligned allocation is useful to prevent cache line sharing.
42 : : */
43 : :
44 : : /*
45 : : * Memory is allocated in fixed size page units - the first page is
46 : : * split between front and back so each get half the page size. If the
47 : : * size is a multiple of 128 then each page offset will be a multiple of
48 : : * 64, which may be useful for sequencing etc.
49 : : */
50 : : #ifndef FLATCC_EMITTER_PAGE_SIZE
51 : : #define FLATCC_EMITTER_MAX_PAGE_SIZE 3000
52 : : #define FLATCC_EMITTER_PAGE_MULTIPLE 64
53 : : #define FLATCC_EMITTER_PAGE_SIZE ((FLATCC_EMITTER_MAX_PAGE_SIZE) &\
54 : : ~(2 * (FLATCC_EMITTER_PAGE_MULTIPLE) - 1))
55 : : #endif
56 : :
57 : : #ifndef FLATCC_EMITTER_ALLOC
58 : : #ifdef FLATCC_EMITTER_USE_ALIGNED_ALLOC
59 : : /*
60 : : * <stdlib.h> does not always provide aligned_alloc, so include whatever
61 : : * is required when enabling this feature.
62 : : */
63 : : #define FLATCC_EMITTER_ALLOC(n) aligned_alloc(FLATCC_EMITTER_PAGE_MULTIPLE,\
64 : : (((n) + FLATCC_EMITTER_PAGE_MULTIPLE - 1) & ~(FLATCC_EMITTER_PAGE_MULTIPLE - 1)))
65 : : #else
66 : : #define FLATCC_EMITTER_ALLOC malloc
67 : : #endif
68 : : #endif
69 : :
70 : : typedef struct flatcc_emitter_page flatcc_emitter_page_t;
71 : : typedef struct flatcc_emitter flatcc_emitter_t;
72 : :
73 : : struct flatcc_emitter_page {
74 : : uint8_t page[FLATCC_EMITTER_PAGE_SIZE];
75 : : flatcc_emitter_page_t *next;
76 : : flatcc_emitter_page_t *prev;
77 : : /*
78 : : * The offset is relative to page start, but not necessarily
79 : : * to any present content if part of front or back page,
80 : : * and undefined for unused pages.
81 : : */
82 : : flatbuffers_soffset_t page_offset;
83 : : };
84 : :
85 : : /*
86 : : * Must be allocated and zeroed externally, e.g. on the stack
87 : : * then provided as emit_context to the flatbuilder along
88 : : * with the `flatcc_emitter` function.
89 : : */
90 : : struct flatcc_emitter {
91 : : flatcc_emitter_page_t *front, *back;
92 : : uint8_t *front_cursor;
93 : : size_t front_left;
94 : : uint8_t *back_cursor;
95 : : size_t back_left;
96 : : size_t used;
97 : : size_t capacity;
98 : : size_t used_average;
99 : : };
100 : :
101 : : /* Optional helper to ensure emitter is zeroed initially. */
102 : : static inline void flatcc_emitter_init(flatcc_emitter_t *E)
103 : : {
104 : : memset(E, 0, sizeof(*E));
105 : : }
106 : :
107 : : /* Deallocates all buffer memory making the emitter ready for next use. */
108 : : void flatcc_emitter_clear(flatcc_emitter_t *E);
109 : :
110 : : /*
111 : : * Similar to `clear_flatcc_emitter` but heuristacally keeps some allocated
112 : : * memory between uses while gradually reducing peak allocations.
113 : : * For small buffers, a single page will remain available with no
114 : : * additional allocations or deallocations after first use.
115 : : */
116 : : void flatcc_emitter_reset(flatcc_emitter_t *E);
117 : :
118 : : /*
119 : : * Helper function that allows a page between front and back to be
120 : : * recycled while the buffer is still being constructed - most likely as part
121 : : * of partial copy or transmission. Attempting to recycle front or back
122 : : * pages will result will result in an error. Recycling pages outside the
123 : : * front and back will be valid but pointless. After recycling and copy
124 : : * operations are no longer well-defined and should be replaced with
125 : : * whatever logic is recycling the pages. The reset operation
126 : : * automatically recycles all (remaining) pages when emission is
127 : : * complete. After recycling, the `flatcc_emitter_size` function will
128 : : * return as if recycle was not called, but will only represent the
129 : : * logical size, not the size of the active buffer. Because a recycled
130 : : * page is fully utilized, it is fairly easy to compensate for this if
131 : : * required.
132 : : *
133 : : * Returns 0 on success.
134 : : */
135 : : int flatcc_emitter_recycle_page(flatcc_emitter_t *E, flatcc_emitter_page_t *p);
136 : :
137 : : /*
138 : : * The amount of data copied with `flatcc_emitter_copy_buffer` and related
139 : : * functions. Normally called at end of buffer construction but is
140 : : * always valid, as is the copy functions. The size is a direct
141 : : * function of the amount emitted data so the flatbuilder itself can
142 : : * also provide this information.
143 : : */
144 : : static inline size_t flatcc_emitter_get_buffer_size(flatcc_emitter_t *E)
145 : : {
146 : : return E->used;
147 : : }
148 : :
149 : : /*
150 : : * Returns buffer start iff the buffer fits on a single internal page.
151 : : * Only useful for fairly small buffers - about half the page size since
152 : : * one half of first page goes to vtables that likely use little space.
153 : : * Returns null if request could not be served.
154 : : *
155 : : * If `size_out` is not null, it is set to the buffer size, or 0 if
156 : : * operation failed.
157 : : */
158 : : static inline void *flatcc_emitter_get_direct_buffer(flatcc_emitter_t *E, size_t *size_out)
159 : : {
160 [ + - ]: 10 : if (E->front == E->back) {
161 [ + + ]: 10 : if (size_out) {
162 : 9 : *size_out = E->used;
163 : : }
164 : 10 : return E->front_cursor;
165 : : }
166 [ # # ]: 0 : if (size_out) {
167 : 0 : *size_out = 0;
168 : : }
169 : : return 0;
170 : : }
171 : :
172 : : /*
173 : : * Copies the internal flatcc_emitter representation to an externally
174 : : * provided linear buffer that must have size `flatcc_emitter_get_size`.
175 : : *
176 : : * If pages have been recycled, only the remaining pages will be copied
177 : : * and thus less data than what `flatcc_emitter_get_size` would suggest. It
178 : : * makes more sense to provide a customized copy operation when
179 : : * recycling pages.
180 : : *
181 : : * If the buffer is too small, nothing is copied, otherwise the
182 : : * full buffer is copied and the input buffer is returned.
183 : : */
184 : : void *flatcc_emitter_copy_buffer(flatcc_emitter_t *E, void *buf, size_t size);
185 : :
186 : : /*
187 : : * The emitter interface function to the flatbuilder API.
188 : : * `emit_context` should be of type `flatcc_emitter_t` for this
189 : : * particular implementation.
190 : : *
191 : : * This function is compatible with the `flatbuilder_emit_fun`
192 : : * type defined in "flatbuilder.h".
193 : : */
194 : : int flatcc_emitter(void *emit_context,
195 : : const flatcc_iovec_t *iov, int iov_count,
196 : : flatbuffers_soffset_t offset, size_t len);
197 : :
198 : : #endif /* FLATCC_EMITTER_H */
|