Branch data Line data Source code
1 : : #include <stdio.h>
2 : : #include <assert.h>
3 : : #include <string.h>
4 : :
5 : : #include "codegen_c.h"
6 : : #include "codegen_c_sort.h"
7 : :
8 : : #define llu(x) (long long unsigned int)(x)
9 : : #define lld(x) (long long int)(x)
10 : :
11 : :
12 : : /*
13 : : * Use of file identifiers for undeclared roots is fuzzy, but we need an
14 : : * identifer for all, so we use the one defined for the current schema
15 : : * file and allow the user to override. This avoids tedious runtime file
16 : : * id arguments to all create calls.
17 : : *
18 : : * As later addition to FlatBuffers, type hashes may replace file
19 : : * identifiers when explicitly stated. These are FNV-1a hashes of the
20 : : * fully qualified type name (dot separated).
21 : : *
22 : : * We generate the type hash both as a native integer constants for use
23 : : * in switch statements, and encoded as a little endian C string for use
24 : : * as a file identifier.
25 : : */
26 : 105 : static void print_type_identifier(fb_output_t *out, const char *name, uint32_t type_hash)
27 : : {
28 : : uint8_t buf[17];
29 : : uint8_t *p;
30 : : uint8_t x;
31 : : int i;
32 : 105 : const char *nsc = out->nsc;
33 : :
34 : 105 : fprintf(out->fp,
35 : : "#ifndef %s_identifier\n"
36 : : "#define %s_identifier %sidentifier\n"
37 : : "#endif\n",
38 : : name, name, nsc);
39 : 105 : fprintf(out->fp,
40 : : "#define %s_type_hash ((%sthash_t)0x%lx)\n",
41 : : name, nsc, (unsigned long)(type_hash));
42 : : p = buf;
43 : : i = 4;
44 [ + + ]: 525 : while (i--) {
45 : 420 : *p++ = '\\';
46 : 420 : *p++ = 'x';
47 : 420 : x = type_hash & 0x0f;
48 [ + + ]: 420 : x += x > 9 ? 'a' - 10 : '0';
49 : 420 : type_hash >>= 4;
50 : 420 : p[1] = x;
51 : 420 : x = type_hash & 0x0f;
52 [ + + ]: 420 : x += x > 9 ? 'a' - 10 : '0';
53 : 420 : type_hash >>= 4;
54 : 420 : p[0] = x;
55 : 420 : p += 2;
56 : : }
57 : 105 : *p = '\0';
58 : 105 : fprintf(out->fp,
59 : : "#define %s_type_identifier \"%s\"\n",
60 : : name, buf);
61 : 105 : }
62 : :
63 : : /* Finds first occurrence of matching key when vector is sorted on the named field. */
64 : 10 : static void gen_find(fb_output_t *out)
65 : : {
66 : 10 : const char *nsc = out->nsc;
67 : :
68 : : /*
69 : : * E: Element accessor (elem = E(vector, index)).
70 : : * A: Field accessor (or the identity function), result must match the diff function D's first arg.
71 : : * V: The vector to search (assuming sorted).
72 : : * T: The scalar, enum or string key type, (either the element, or a field of the element).
73 : : * K: The search key.
74 : : * Kn: optional key length so external strings do not have to be zero terminated.
75 : : * D: the diff function D(v, K, Kn) :: v - <K, Kn>
76 : : *
77 : : * returns index (0..len - 1), or not_found (-1).
78 : : */
79 : 10 : fprintf(out->fp,
80 : : "#include <string.h>\n"
81 : : "static size_t %snot_found = (size_t)-1;\n"
82 : : "#define __%sidentity(n) (n)\n",
83 : : nsc, nsc);
84 : 10 : fprintf(out->fp,
85 : : "/* Subtraction doesn't work for unsigned types. */\n"
86 : : "#define __%sscalar_cmp(x, y, n) ((x) < (y) ? -1 : (x) > (y))\n"
87 : : "static inline int __%sstring_n_cmp(%sstring_t v, const char *s, size_t n)\n"
88 : : "{ size_t nv = %sstring_len(v); int x = strncmp(v, s, nv < n ? nv : n);\n"
89 : : " return x != 0 ? x : nv < n ? -1 : nv > n; }\n"
90 : : "/* `n` arg unused, but needed by string find macro expansion. */\n"
91 : : "static inline int __%sstring_cmp(%sstring_t v, const char *s, size_t n) { (void)n; return strcmp(v, s); }\n",
92 : : nsc, nsc, nsc, nsc, nsc, nsc);
93 : 10 : fprintf(out->fp,
94 : : "/* A = identity if searching scalar vectors rather than key fields. */\n"
95 : : "/* Returns lowest matching index not_found. */\n"
96 : : "#define __%sfind_by_field(A, V, E, L, K, Kn, T, D)\\\n"
97 : : "{ T v; size_t a = 0, b, m; if (!(b = L(V))) { return %snot_found; }\\\n"
98 : : " --b;\\\n"
99 : : " while (a < b) {\\\n"
100 : : " m = a + ((b - a) >> 1);\\\n"
101 : : " v = A(E(V, m));\\\n"
102 : : " if ((D(v, (K), (Kn))) < 0) {\\\n"
103 : : " a = m + 1;\\\n"
104 : : " } else {\\\n"
105 : : " b = m;\\\n"
106 : : " }\\\n"
107 : : " }\\\n"
108 : : " if (a == b) {\\\n"
109 : : " v = A(E(V, a));\\\n"
110 : : " if (D(v, (K), (Kn)) == 0) {\\\n"
111 : : " return a;\\\n"
112 : : " }\\\n"
113 : : " }\\\n"
114 : : " return %snot_found;\\\n"
115 : : "}\n",
116 : : nsc, nsc, nsc);
117 : 10 : fprintf(out->fp,
118 : : "#define __%sfind_by_scalar_field(A, V, E, L, K, T)\\\n"
119 : : "__%sfind_by_field(A, V, E, L, K, 0, T, __%sscalar_cmp)\n"
120 : : "#define __%sfind_by_string_field(A, V, E, L, K)\\\n"
121 : : "__%sfind_by_field(A, V, E, L, K, 0, %sstring_t, __%sstring_cmp)\n"
122 : : "#define __%sfind_by_string_n_field(A, V, E, L, K, Kn)\\\n"
123 : : "__%sfind_by_field(A, V, E, L, K, Kn, %sstring_t, __%sstring_n_cmp)\n",
124 : : nsc, nsc, nsc, nsc, nsc,
125 : : nsc, nsc, nsc, nsc, nsc, nsc);
126 : 10 : fprintf(out->fp,
127 : : "#define __%sdefine_find_by_scalar_field(N, NK, TK)\\\n"
128 : : "static inline size_t N ## _vec_find_by_ ## NK(N ## _vec_t vec, TK key)\\\n"
129 : : "__%sfind_by_scalar_field(N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, key, TK)\n",
130 : : nsc, nsc);
131 : 10 : fprintf(out->fp,
132 : : "#define __%sdefine_scalar_find(N, T)\\\n"
133 : : "static inline size_t N ## _vec_find(N ## _vec_t vec, T key)\\\n"
134 : : "__%sfind_by_scalar_field(__%sidentity, vec, N ## _vec_at, N ## _vec_len, key, T)\n",
135 : : nsc, nsc, nsc);
136 : 10 : }
137 : :
138 : 10 : static void gen_helpers(fb_output_t *out)
139 : : {
140 : 10 : const char *nsc = out->nsc;
141 : :
142 : 10 : fprintf(out->fp,
143 : : /*
144 : : * Include the basic primitives for accessing flatbuffer datatypes independent
145 : : * of endianness.
146 : : *
147 : : * The included file must define the basic types and accessors
148 : : * prefixed with the common namespace which by default is
149 : : * "flatbuffers_".
150 : : */
151 : : "#include \"flatcc/flatcc_flatbuffers.h\"\n"
152 : : "\n\n");
153 : : /*
154 : : * The remapping of basic types to the common namespace makes it
155 : : * possible to have different definitions. The generic
156 : : * `flatbuffers_uoffset_t` etc. cannot be trusted to have one specific
157 : : * size since it depends on the included `flatcc/flatcc_types.h`
158 : : * filer, but the namespace prefixed types can be trusted if used carefully.
159 : : * For example the common namespace could be `flatbuffers_large_`
160 : : * when allowing for 64 bit offsets.
161 : : */
162 [ - + ]: 10 : if (strcmp(nsc, "flatbuffers_")) {
163 : 0 : fprintf(out->fp,
164 : : "typedef flatbuffers_uoffset_t %suoffset_t;\n"
165 : : "typedef flatbuffers_soffset_t %ssoffset_t;\n"
166 : : "typedef flatbuffers_voffset_t %svoffset_t;\n"
167 : : "typedef flatbuffers_utype_t %sutype_t;\n"
168 : : "typedef flatbuffers_bool_t %sbool_t;\n"
169 : : "\n",
170 : : nsc, nsc, nsc, nsc, nsc);
171 : 0 : fprintf(out->fp,
172 : : "#define %sendian flatbuffers_endian\n"
173 : : "__flatcc_define_basic_scalar_accessors(%s, flatbuffers_endian)"
174 : : "__flatcc_define_integer_accessors(%sbool, flatbuffers_bool_t,\\\n"
175 : : " FLATBUFFERS_BOOL_WIDTH, flatbuffers_endian)\n",
176 : : nsc, nsc, nsc);
177 : 0 : fprintf(out->fp,
178 : : "__flatcc_define_integer_accessors(__%suoffset, flatbuffers_uoffset_t,\n"
179 : : " FLATBUFFERS_UOFFSET_WIDTH, flatbuffers_endian)\n"
180 : : "__flatcc_define_integer_accessors(__%ssoffset, flatbuffers_soffset_t,\n"
181 : : " FLATBUFFERS_SOFFSET_WIDTH, flatbuffers_endian)\n"
182 : : "__flatcc_define_integer_accessors(__%svoffset, flatbuffers_voffset_t,\n"
183 : : " FLATBUFFERS_VOFFSET_WIDTH, flatbuffers_endian)\n"
184 : : "__flatcc_define_integer_accessors(__%sutype, flatbuffers_utype_t,\n"
185 : : " FLATBUFFERS_UTYPE_WIDTH, flatbuffers_endian)\n"
186 : : "__flatcc_define_integer_accessors(__%sthash, flatbuffers_thash_t,\n"
187 : : " FLATBUFFERS_THASH_WIDTH, flatbuffers_endian)\n",
188 : : nsc, nsc, nsc, nsc, nsc);
189 : 0 : fprintf(out->fp,
190 : : "#ifndef %s_WRAP_NAMESPACE\n"
191 : : "#define %s_WRAP_NAMESPACE(ns, x) ns ## _ ## x\n"
192 : : "#endif\n",
193 : 0 : out->nscup, out->nscup);
194 : : }
195 : : /* Build out a more elaborate type system based in the primitives included. */
196 : 10 : fprintf(out->fp,
197 : : "#define __%sread_scalar_at_byteoffset(N, p, o) N ## _read_from_pe((uint8_t *)(p) + (o))\n"
198 : : "#define __%sread_scalar(N, p) N ## _read_from_pe(p)\n",
199 : : nsc, nsc);
200 : 10 : fprintf(out->fp,
201 : : "#define __%sread_vt(ID, offset, t)\\\n"
202 : : "%svoffset_t offset = 0;\\\n"
203 : : "{ %svoffset_t id, *vt;\\\n"
204 : : " assert(t != 0 && \"null pointer table access\");\\\n"
205 : : " id = ID;\\\n"
206 : : " vt = (%svoffset_t *)((uint8_t *)(t) -\\\n"
207 : : " __%ssoffset_read_from_pe(t));\\\n"
208 : : " if (__%svoffset_read_from_pe(vt) >= sizeof(vt[0]) * (id + 3)) {\\\n"
209 : : " offset = __%svoffset_read_from_pe(vt + id + 2);\\\n"
210 : : " }\\\n"
211 : : "}\n",
212 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc);
213 : 10 : fprintf(out->fp,
214 : : "#define __%sfield_present(ID, t) { __%sread_vt(ID, offset, t) return offset != 0; }\n",
215 : : nsc, nsc);
216 : 10 : fprintf(out->fp,
217 : : "#define __%sscalar_field(N, ID, V, t)\\\n"
218 : : "{\\\n"
219 : : " __%sread_vt(ID, offset, t)\\\n"
220 : : " return offset ? __%sread_scalar_at_byteoffset(N, t, offset) : V;\\\n"
221 : : "}\n",
222 : : nsc, nsc, nsc);
223 : 10 : fprintf(out->fp,
224 : : "#define __%sstruct_field(T, ID, t, r)\\\n"
225 : : "{\\\n"
226 : : " __%sread_vt(ID, offset, t)\\\n"
227 : : " if (offset) {\\\n"
228 : : " return (T)((uint8_t *)(t) + offset);\\\n"
229 : : " }\\\n"
230 : : " assert(!(r) && \"required field missing\");\\\n"
231 : : " return 0;\\\n"
232 : : "}\n",
233 : : nsc, nsc);
234 : 10 : fprintf(out->fp,
235 : : "#define __%soffset_field(T, ID, t, r, adjust)\\\n"
236 : : "{\\\n"
237 : : " %suoffset_t *elem;\\\n"
238 : : " __%sread_vt(ID, offset, t)\\\n"
239 : : " if (offset) {\\\n"
240 : : " elem = (%suoffset_t *)((uint8_t *)(t) + offset);\\\n"
241 : : " /* Add sizeof so C api can have raw access past header field. */\\\n"
242 : : " return (T)((uint8_t *)(elem) + adjust +\\\n"
243 : : " __%suoffset_read_from_pe(elem));\\\n"
244 : : " }\\\n"
245 : : " assert(!(r) && \"required field missing\");\\\n"
246 : : " return 0;\\\n"
247 : : "}\n",
248 : : nsc, nsc, nsc, nsc, nsc);
249 : 10 : fprintf(out->fp,
250 : : "#define __%svector_field(T, ID, t, r) __%soffset_field(T, ID, t, r, sizeof(%suoffset_t))\n"
251 : : "#define __%stable_field(T, ID, t, r) __%soffset_field(T, ID, t, r, 0)\n",
252 : : nsc, nsc, nsc, nsc, nsc);
253 : 10 : fprintf(out->fp,
254 : : "#define __%svec_len(vec)\\\n"
255 : : "{ return (vec) ? (size_t)__%suoffset_read_from_pe((flatbuffers_uoffset_t *)vec - 1) : 0; }\n"
256 : : "#define __%sstring_len(s) __%svec_len(s)\n",
257 : : nsc, nsc, nsc, nsc);
258 : 10 : fprintf(out->fp,
259 : : "static inline size_t %svec_len(const void *vec)\n"
260 : : "__%svec_len(vec)\n",
261 : : nsc, nsc);
262 : 10 : fprintf(out->fp,
263 : : /* Tb is the base type for loads. */
264 : : "#define __%sscalar_vec_at(N, vec, i)\\\n"
265 : : "{ assert(%svec_len(vec) > (i) && \"index out of range\");\\\n"
266 : : " return __%sread_scalar(N, &(vec)[i]); }\n",
267 : : nsc, nsc, nsc);
268 : 10 : fprintf(out->fp,
269 : : "#define __%sstruct_vec_at(vec, i)\\\n"
270 : : "{ assert(%svec_len(vec) > (i) && \"index out of range\"); return (vec) + (i); }\n",
271 : : nsc, nsc);
272 : 10 : fprintf(out->fp,
273 : : "/* `adjust` skips past the header for string vectors. */\n"
274 : : "#define __%soffset_vec_at(T, vec, i, adjust)\\\n"
275 : : "{ const %suoffset_t *elem = (vec) + (i);\\\n"
276 : : " assert(%svec_len(vec) > (i) && \"index out of range\");\\\n"
277 : : " return (T)((uint8_t *)(elem) + (size_t)__%suoffset_read_from_pe(elem) + adjust); }\n",
278 : : nsc, nsc, nsc, nsc);
279 : 10 : fprintf(out->fp,
280 : : "#define __%sdefine_scalar_vec_len(N) \\\n"
281 : : "static inline size_t N ## _vec_len(N ##_vec_t vec)\\\n"
282 : : "{ return %svec_len(vec); }\n",
283 : : nsc, nsc);
284 : 10 : fprintf(out->fp,
285 : : "#define __%sdefine_scalar_vec_at(N, T) \\\n"
286 : : "static inline T N ## _vec_at(N ## _vec_t vec, size_t i)\\\n"
287 : : "__%sscalar_vec_at(N, vec, i)\n",
288 : : nsc, nsc);
289 : 10 : fprintf(out->fp,
290 : : "typedef const char *%sstring_t;\n"
291 : : "static inline size_t %sstring_len(%sstring_t s)\n"
292 : : "__%sstring_len(s)\n",
293 : : nsc, nsc, nsc, nsc);
294 : 10 : fprintf(out->fp,
295 : : "typedef const %suoffset_t *%sstring_vec_t;\n"
296 : : "typedef %suoffset_t *%sstring_mutable_vec_t;\n"
297 : : "static inline size_t %sstring_vec_len(%sstring_vec_t vec)\n"
298 : : "__%svec_len(vec)\n"
299 : : "static inline %sstring_t %sstring_vec_at(%sstring_vec_t vec, size_t i)\n"
300 : : "__%soffset_vec_at(%sstring_t, vec, i, sizeof(vec[0]))\n",
301 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc);
302 : 10 : fprintf(out->fp,
303 : : "typedef const void *%sgeneric_table_t;\n",
304 : : nsc);
305 : 10 : gen_find(out);
306 [ + - ]: 10 : if (out->opts->cgen_sort) {
307 : 10 : gen_sort(out);
308 : : } else {
309 : 0 : fprintf(out->fp, "/* sort disabled */\n");
310 : : }
311 : 10 : fprintf(out->fp,
312 : : "#define __%sdefine_scalar_vector(N, T)\\\n"
313 : : "typedef const T *N ## _vec_t;\\\n"
314 : : "typedef T *N ## _mutable_vec_t;\\\n"
315 : : "__%sdefine_scalar_vec_len(N)\\\n"
316 : : "__%sdefine_scalar_vec_at(N, T)\\\n"
317 : : "__%sdefine_scalar_find(N, T)\\\n",
318 : : nsc, nsc, nsc, nsc);
319 [ + - ]: 10 : if (out->opts->cgen_sort) {
320 : 10 : fprintf(out->fp, "\\\n__%sdefine_scalar_sort(N, T)\n", nsc);
321 : : }
322 : 10 : fprintf(out->fp, "\n");
323 : : /* Elaborate on the included basic type system. */
324 : 10 : fprintf(out->fp,
325 : : "#define __%sdefine_integer_type(N, T, W)\\\n"
326 : : "__flatcc_define_integer_accessors(N, T, W, %sendian)\\\n"
327 : : "__%sdefine_scalar_vector(N, T)\n",
328 : : nsc, nsc, nsc);
329 : 10 : fprintf(out->fp,
330 : : "__%sdefine_scalar_vector(%sbool, %sbool_t)\n"
331 : : "__%sdefine_scalar_vector(%suint8, uint8_t)\n"
332 : : "__%sdefine_scalar_vector(%sint8, int8_t)\n"
333 : : "__%sdefine_scalar_vector(%suint16, uint16_t)\n"
334 : : "__%sdefine_scalar_vector(%sint16, int16_t)\n"
335 : : "__%sdefine_scalar_vector(%suint32, uint32_t)\n"
336 : : "__%sdefine_scalar_vector(%sint32, int32_t)\n"
337 : : "__%sdefine_scalar_vector(%suint64, uint64_t)\n"
338 : : "__%sdefine_scalar_vector(%sint64, int64_t)\n"
339 : : "__%sdefine_scalar_vector(%sfloat, float)\n"
340 : : "__%sdefine_scalar_vector(%sdouble, double)\n",
341 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc,
342 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc);
343 : 10 : fprintf(out->fp,
344 : : "static inline size_t %sstring_vec_find(%sstring_vec_t vec, const char *s)\n"
345 : : "__%sfind_by_string_field(__%sidentity, vec, %sstring_vec_at, %sstring_vec_len, s)\n"
346 : : "static inline size_t %sstring_vec_find_n(%sstring_vec_t vec, const char *s, size_t n)\n"
347 : : "__%sfind_by_string_n_field(__%sidentity, vec, %sstring_vec_at, %sstring_vec_len, s, n)\n",
348 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc);
349 [ + - ]: 10 : if (out->opts->cgen_sort) {
350 : 10 : fprintf(out->fp, "__%sdefine_string_sort()\n", nsc);
351 : : }
352 : 10 : fprintf(out->fp,
353 : : "#define __%sstruct_scalar_field(t, M, N)\\\n"
354 : : "{ return t ? __%sread_scalar(N, &(t->M)) : 0; }\n"
355 : : "#define __%sstruct_struct_field(t, M) { return t ? &(t->M) : 0; }\n",
356 : : nsc, nsc, nsc);
357 : 10 : fprintf(out->fp,
358 : : "/* If fid is null, the function returns true without testing as buffer is not expected to have any id. */\n"
359 : : "static inline int %shas_identifier(const void *buffer, const char *fid)\n"
360 : : "{ %sthash_t id, id2 = 0; if (fid == 0) { return 1; };\n"
361 : : " strncpy((char *)&id2, fid, sizeof(id2));\n"
362 : : " /* Identifier strings are always considered little endian. */\n"
363 : : " id2 = __%sthash_cast_from_le(id2);\n"
364 : : " id = __%sthash_read_from_pe(((%suoffset_t *)buffer) + 1);\n"
365 : : " return id2 == 0 || id == id2; }\n"
366 : : "static inline int %shas_type_hash(const void *buffer, %sthash_t thash)\n"
367 : : "{ return thash == 0 || (__%sthash_read_from_pe((%suoffset_t *)buffer + 1) == thash); }\n\n"
368 : : "static inline %sthash_t %sget_type_hash(const void *buffer)\n"
369 : : "{ return __%sthash_read_from_pe((flatbuffers_uoffset_t *)buffer + 1); }\n\n"
370 : : "#define %sverify_endian() %shas_identifier(\"\\x00\\x00\\x00\\x00\" \"1234\", \"1234\")\n",
371 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc);
372 : 10 : fprintf(out->fp,
373 : : "static inline void *%sread_size_prefix(void *b, size_t *size_out)\n"
374 : : "{ if (size_out) { *size_out = (size_t)__%suoffset_read_from_pe(b); }\n"
375 : : " return (uint8_t *)b + sizeof(%suoffset_t); }\n", nsc, nsc, nsc);
376 : 10 : fprintf(out->fp,
377 : : "/* Null file identifier accepts anything, otherwise fid should be 4 characters. */\n"
378 : : "#define __%sread_root(T, K, buffer, fid)\\\n"
379 : : " ((!buffer || !%shas_identifier(buffer, fid)) ? 0 :\\\n"
380 : : " ((T ## _ ## K ## t)(((uint8_t *)buffer) +\\\n"
381 : : " __%suoffset_read_from_pe(buffer))))\n"
382 : : "#define __%sread_typed_root(T, K, buffer, thash)\\\n"
383 : : " ((!buffer || !%shas_type_hash(buffer, thash)) ? 0 :\\\n"
384 : : " ((T ## _ ## K ## t)(((uint8_t *)buffer) +\\\n"
385 : : " __%suoffset_read_from_pe(buffer))))\n",
386 : : nsc, nsc, nsc, nsc, nsc, nsc);
387 : 10 : fprintf(out->fp,
388 : : "#define __%snested_buffer_as_root(C, N, T, K)\\\n"
389 : : "static inline T ## _ ## K ## t C ## _ ## N ## _as_root_with_identifier(C ## _ ## table_t t, const char *fid)\\\n"
390 : : "{ const uint8_t *buffer = C ## _ ## N(t); return __%sread_root(T, K, buffer, fid); }\\\n"
391 : : "static inline T ## _ ## K ## t C ## _ ## N ## _as_typed_root(C ## _ ## table_t t)\\\n"
392 : : "{ const uint8_t *buffer = C ## _ ## N(t); return __%sread_root(T, K, buffer, C ## _ ## type_identifier); }\\\n"
393 : : "static inline T ## _ ## K ## t C ## _ ## N ## _as_root(C ## _ ## table_t t)\\\n"
394 : : "{ const char *fid = T ## _identifier;\\\n"
395 : : " const uint8_t *buffer = C ## _ ## N(t); return __%sread_root(T, K, buffer, fid); }\n",
396 : : nsc, nsc, nsc, nsc);
397 : 10 : fprintf(out->fp,
398 : : "#define __%sbuffer_as_root(N, K)\\\n"
399 : : "static inline N ## _ ## K ## t N ## _as_root_with_identifier(const void *buffer, const char *fid)\\\n"
400 : : "{ return __%sread_root(N, K, buffer, fid); }\\\n"
401 : : "static inline N ## _ ## K ## t N ## _as_root_with_type_hash(const void *buffer, %sthash_t thash)\\\n"
402 : : "{ return __%sread_typed_root(N, K, buffer, thash); }\\\n"
403 : : "static inline N ## _ ## K ## t N ## _as_root(const void *buffer)\\\n"
404 : : "{ const char *fid = N ## _identifier;\\\n"
405 : : " return __%sread_root(N, K, buffer, fid); }\\\n"
406 : : "static inline N ## _ ## K ## t N ## _as_typed_root(const void *buffer)\\\n"
407 : : "{ return __%sread_typed_root(N, K, buffer, N ## _type_hash); }\n"
408 : : "#define __%sstruct_as_root(N) __%sbuffer_as_root(N, struct_)\n"
409 : : "#define __%stable_as_root(N) __%sbuffer_as_root(N, table_)\n",
410 : : nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc, nsc);
411 : 10 : fprintf(out->fp, "\n");
412 : 10 : }
413 : :
414 : 10 : int fb_gen_common_c_header(fb_output_t *out)
415 : : {
416 : 10 : const char *nscup = out->nscup;
417 : :
418 : 10 : fprintf(out->fp,
419 : : "#ifndef %s_COMMON_READER_H\n"
420 : : "#define %s_COMMON_READER_H\n",
421 : : nscup, nscup);
422 : 10 : fprintf(out->fp, "\n/* " FLATCC_GENERATED_BY " */\n\n");
423 : 10 : fprintf(out->fp, "/* Common FlatBuffers read functionality for C. */\n\n");
424 [ - + ]: 10 : if (!out->opts->cgen_sort) {
425 : 0 : fprintf(out->fp,
426 : : "/*"
427 : : " * This code is generated without support for vector sort operations\n"
428 : : " * but find operations are supported on pre-sorted vectors.\n"
429 : : " */\n");
430 : : }
431 : : gen_pragma_push(out);
432 : 10 : gen_helpers(out);
433 : : gen_pragma_pop(out);
434 : 10 : fprintf(out->fp,
435 : : "#endif /* %s_COMMON_H */\n",
436 : : nscup);
437 : 10 : return 0;
438 : : }
439 : :
440 : 24 : static void gen_pretext(fb_output_t *out)
441 : : {
442 : 24 : const char *nsc = out->nsc;
443 : 24 : const char *nscup = out->nscup;
444 : :
445 : 24 : int do_pad = out->opts->cgen_pad;
446 : :
447 : 24 : fprintf(out->fp,
448 : : "#ifndef %s_READER_H\n"
449 : : "#define %s_READER_H\n",
450 : 24 : out->S->basenameup, out->S->basenameup);
451 : :
452 : 24 : fprintf(out->fp, "\n/* " FLATCC_GENERATED_BY " */\n\n");
453 [ - + ]: 24 : if (do_pad) {
454 : 0 : fprintf(out->fp,
455 : : "/*\n"
456 : : " * Generated with 'pad' option which expects #pragma pack(1) and\n"
457 : : " * #pragma pack() to be supported, and which adds extra padding\n"
458 : : " * fields to structs.\n"
459 : : " *\n"
460 : : " * This is mostly relevant for some micro controller platforms, but\n"
461 : : " * may also be needed with 'force_align' attributes > 16.\n"
462 : : " *\n"
463 : : " * The default output uses C11 <stdalign.h> alignas(n) which can be\n"
464 : : " * defined as `__attribute__((aligned (n)))` or similar on many\n"
465 : : " * older platforms.\n"
466 : : " */\n"
467 : : "\n");
468 : : }
469 : :
470 : 24 : fprintf(out->fp,
471 : : "#ifndef %s_COMMON_READER_H\n"
472 : : "#include \"%scommon_reader.h\"\n"
473 : : "#endif\n",
474 : : nscup, nsc);
475 : 24 : fb_gen_c_includes(out, "_reader.h", "_READER_H");
476 : :
477 [ + - ]: 24 : if (!do_pad) {
478 : 24 : fprintf(out->fp,
479 : : "#ifndef alignas\n"
480 : : "#include <stdalign.h>\n"
481 : : "#endif\n");
482 : : }
483 : : gen_pragma_push(out);
484 [ + + ]: 24 : if (out->S->file_identifier.type == vt_string) {
485 : 8 : fprintf(out->fp,
486 : : "#undef %sidentifier\n"
487 : : "#define %sidentifier \"%.*s\"\n",
488 : : nsc,
489 : : nsc, out->S->file_identifier.s.len, out->S->file_identifier.s.s);
490 : : } else {
491 : 16 : fprintf(out->fp,
492 : : "#ifndef %sidentifier\n"
493 : : "#define %sidentifier 0\n"
494 : : "#endif\n",
495 : : nsc, nsc);
496 : : }
497 [ + + ]: 24 : if (out->S->file_extension.type == vt_string) {
498 : 8 : fprintf(out->fp,
499 : : "#undef %sextension\n"
500 : : "#define %sextension \".%.*s\"\n",
501 : : nsc,
502 : : nsc, out->S->file_extension.s.len, out->S->file_extension.s.s);
503 : : } else {
504 : 16 : fprintf(out->fp,
505 : : /* Configured extensions include dot, schema does not. */
506 : : "#ifndef %sextension\n"
507 : : "#define %sextension \"%s\"\n"
508 : : "#endif\n",
509 : 16 : nsc, nsc, out->opts->default_bin_ext);
510 : : }
511 : 24 : fprintf(out->fp, "\n");
512 : 24 : }
513 : :
514 : 24 : static void gen_footer(fb_output_t *out)
515 : : {
516 : : gen_pragma_pop(out);
517 : 24 : fprintf(out->fp, "#endif /* %s_READER_H */\n", out->S->basenameup);
518 : 24 : }
519 : :
520 : 105 : static void gen_forward_decl(fb_output_t *out, fb_compound_type_t *ct)
521 : : {
522 : : fb_scoped_name_t snt;
523 : 105 : const char *nsc = out->nsc;
524 : :
525 : 105 : fb_clear(snt);
526 : :
527 : : assert(ct->symbol.kind == fb_is_struct || ct->symbol.kind == fb_is_table);
528 : :
529 : : fb_compound_name(ct, &snt);
530 [ + + ]: 105 : if (ct->symbol.kind == fb_is_struct) {
531 [ + + ]: 42 : if (ct->size == 0) {
532 : 21 : fprintf(out->fp, "typedef void %s_t; /* empty struct */\n",
533 : : snt.text);
534 : : } else {
535 : 21 : fprintf(out->fp, "typedef struct %s %s_t;\n",
536 : : snt.text, snt.text);
537 : : }
538 : 42 : fprintf(out->fp, "typedef const %s_t *%s_struct_t;\n",
539 : : snt.text, snt.text);
540 : 42 : fprintf(out->fp, "typedef %s_t *%s_mutable_struct_t;\n",
541 : : snt.text, snt.text);
542 : 42 : fprintf(out->fp, "typedef const %s_t *%s_vec_t;\n",
543 : : snt.text, snt.text);
544 : 42 : fprintf(out->fp, "typedef %s_t *%s_mutable_vec_t;\n",
545 : : snt.text, snt.text);
546 : : } else {
547 : 63 : fprintf(out->fp, "typedef const struct %s_table *%s_table_t;\n",
548 : : snt.text, snt.text);
549 : 63 : fprintf(out->fp, "typedef const %suoffset_t *%s_vec_t;\n", nsc, snt.text);
550 : 63 : fprintf(out->fp, "typedef %suoffset_t *%s_mutable_vec_t;\n", nsc, snt.text);
551 : : }
552 : 105 : }
553 : :
554 : 1306 : static inline void print_doc(fb_output_t *out, const char *indent, fb_doc_t *doc)
555 : : {
556 : : long ln = 0;
557 : : int first = 1;
558 [ + + ]: 653 : if (doc == 0) {
559 : : return;
560 : : }
561 [ + + ]: 58 : while (doc) {
562 [ + + ]: 45 : if (ln != doc->ident->linenum) {
563 [ + + ]: 23 : if (first) {
564 : : /* Not all C compilers understand // comments. */
565 : 13 : fprintf(out->fp, "%s/**", indent);
566 : : ln = doc->ident->linenum;
567 : : } else {
568 : 10 : fprintf(out->fp, "\n%s * ", indent);
569 : : }
570 : : }
571 : : first = 0;
572 : 45 : fprintf(out->fp, "%.*s", (int)doc->ident->len, doc->ident->text);
573 : 45 : ln = doc->ident->linenum;
574 : 45 : doc = doc->link;
575 : : }
576 : 13 : fprintf(out->fp, " */\n");
577 : : }
578 : :
579 : 42 : static void gen_struct(fb_output_t *out, fb_compound_type_t *ct)
580 : : {
581 : : fb_member_t *member;
582 : : fb_symbol_t *sym;
583 : : unsigned align;
584 : : size_t offset = 0;
585 : : const char *tname, *tname_ns, *tname_prefix;
586 : : int n;
587 : : const char *s;
588 : : unsigned pad_index = 0, deprecated_index = 0, pad;
589 : : const char *kind;
590 : 42 : int do_pad = out->opts->cgen_pad;
591 : : int current_key_processed, already_has_key;
592 : 42 : const char *nsc = out->nsc;
593 : :
594 : : fb_scoped_name_t snt;
595 : : fb_scoped_name_t snref;
596 : :
597 : 42 : fb_clear(snt);
598 : 42 : fb_clear(snref);
599 : :
600 : : assert(ct->symbol.kind == fb_is_struct);
601 : : assert(ct->align > 0 || ct->count == 0);
602 : : assert(ct->size > 0 || ct->count == 0);
603 : :
604 : : fb_compound_name(ct, &snt);
605 : 42 : print_doc(out, "", ct->doc);
606 [ + + ]: 42 : if (ct->size == 0) {
607 : : /*
608 : : * This implies that sizeof(typename) is not valid, where
609 : : * non-std gcc extension might return 0, or 1 of an empty
610 : : * struct. All copy_from/to etc. operations on this type
611 : : * just returns a pointer without using sizeof.
612 : : *
613 : : * We ought to define size as a define so it can be used in a
614 : : * switch, but that does not mesth with flatcc_accessors.h
615 : : * macros, so we use an inline function. Users would normally
616 : : * use sizeof which will break for empty which is ok, and
617 : : * internal operations can use size() where generic behavior is
618 : : * required.
619 : : */
620 : 21 : fprintf(out->fp, "/* empty struct already typedef'ed as void since this not permitted in std. C: struct %s {}; */\n", snt.text);
621 : 21 : fprintf(out->fp,
622 : : "static inline const %s_t *%s__const_ptr_add(const %s_t *p, size_t i) { return p; }\n", snt.text, snt.text, snt.text);
623 : 21 : fprintf(out->fp,
624 : : "static inline %s_t *%s__ptr_add(%s_t *p, size_t i) { return p; }\n", snt.text, snt.text, snt.text);
625 : 21 : fprintf(out->fp,
626 : : "static inline %s_struct_t %s_vec_at(%s_vec_t vec, size_t i) { return vec; }\n", snt.text, snt.text, snt.text);
627 : : } else {
628 [ - + ]: 21 : if (do_pad) {
629 : 0 : fprintf(out->fp, "#pragma pack(1)\n");
630 : : }
631 : : /*
632 : : * Unfortunately the following is not valid in C11:
633 : : *
634 : : * struct alignas(4) mystruct { ... };
635 : : *
636 : : * we can only use alignas on members (unlike C++, and unlike
637 : : * non-portable C compiler variants).
638 : : *
639 : : * By padding the first element to the struct size we get around
640 : : * this problem. It shouldn't strictly be necessary to add padding
641 : : * fields, but compilers might not support padding above 16 bytes,
642 : : * so we do that as a precaution with an optional compiler flag.
643 : : *
644 : : * It is unclear how to align empty structs without padding but it
645 : : * shouldn't really matter since not field is accessed then.
646 : : */
647 : 21 : fprintf(out->fp, "struct %s {\n", snt.text);
648 : : already_has_key = 0;
649 [ + + ]: 95 : for (sym = ct->members; sym; sym = sym->link) {
650 : : current_key_processed = 0;
651 : : member = (fb_member_t *)sym;
652 : 74 : print_doc(out, " ", member->doc);
653 : : symbol_name(sym, &n, &s);
654 [ + + ]: 74 : align = offset == 0 ? ct->align : member->align;
655 [ - + ][ # # ]: 74 : if (do_pad && (pad = (unsigned)(member->offset - offset))) {
656 : 0 : fprintf(out->fp, " uint8_t __padding%u[%u];\n",
657 : : pad_index++, pad);
658 : : }
659 [ + + ]: 74 : if (member->metadata_flags & fb_f_deprecated) {
660 : 1 : pad = (unsigned)member->size;
661 [ - + ]: 1 : if (do_pad) {
662 : 0 : fprintf(out->fp, " uint8_t __deprecated%u[%u]; /* was: '%.*s' */\n",
663 : : deprecated_index++, pad, n, s);
664 : : } else {
665 : 1 : fprintf(out->fp, " alignas(%u) uint8_t __deprecated%u[%u]; /* was: '%.*s' */\n",
666 : : align, deprecated_index++, pad, n, s);
667 : : }
668 : 1 : offset = (unsigned)(member->offset + member->size);
669 : 1 : continue;
670 : : }
671 [ + + - ]: 73 : switch (member->type.type) {
672 : : case vt_scalar_type:
673 : 53 : tname_ns = scalar_type_ns(member->type.st, nsc);
674 : 53 : tname = scalar_type_name(member->type.st);
675 [ - + ]: 53 : if (do_pad) {
676 : 0 : fprintf(out->fp, " %s%s ", tname_ns, tname);
677 : : } else {
678 : 53 : fprintf(out->fp, " alignas(%u) %s%s ", align, tname_ns, tname);
679 : : }
680 : : break;
681 : : case vt_compound_type_ref:
682 : : assert(member->type.ct->symbol.kind == fb_is_struct || member->type.ct->symbol.kind == fb_is_enum);
683 [ + + ]: 20 : kind = member->type.ct->symbol.kind == fb_is_struct ? "" : "enum_";
684 : : fb_compound_name(member->type.ct, &snref);
685 [ - + ]: 20 : if (do_pad) {
686 : 0 : fprintf(out->fp, " %s_%st ", snref.text, kind);
687 : : } else {
688 : 20 : fprintf(out->fp, " alignas(%u) %s_%st ", align, snref.text, kind);
689 : : }
690 : : break;
691 : : default:
692 : 0 : fprintf(out->fp, " %s ", __FLATCC_ERROR_TYPE);
693 : 0 : gen_panic(out, "internal error: unexpected type during code generation");
694 : : break;
695 : : }
696 : 73 : fprintf(out->fp, "%.*s;\n", n, s);
697 : 73 : offset = (unsigned)(member->offset + member->size);
698 : : }
699 [ - + ][ # # ]: 21 : if (do_pad && (pad = (unsigned)(ct->size - offset))) {
700 : 0 : fprintf(out->fp, " uint8_t __padding%u[%u];\n",
701 : : pad_index, pad);
702 : : }
703 : 21 : fprintf(out->fp, "};\n");
704 [ - + ]: 21 : if (do_pad) {
705 : 0 : fprintf(out->fp, "#pragma pack()\n");
706 : : }
707 : 21 : fprintf(out->fp,
708 : : "static_assert(sizeof(%s_t) == %llu, \"struct size mismatch\");\n\n",
709 : 21 : snt.text, llu(ct->size));
710 : 21 : fprintf(out->fp,
711 : : "static inline const %s_t *%s__const_ptr_add(const %s_t *p, size_t i) { return p + i; }\n", snt.text, snt.text, snt.text);
712 : 21 : fprintf(out->fp,
713 : : "static inline %s_t *%s__ptr_add(%s_t *p, size_t i) { return p + i; }\n", snt.text, snt.text, snt.text);
714 : 21 : fprintf(out->fp,
715 : : "static inline %s_struct_t %s_vec_at(%s_vec_t vec, size_t i)\n"
716 : : "__%sstruct_vec_at(vec, i)\n",
717 : : snt.text, snt.text, snt.text,
718 : : nsc);
719 : : }
720 : 42 : fprintf(out->fp, "static inline size_t %s__size() { return %llu; }\n",
721 : 42 : snt.text, llu(ct->size));
722 : 42 : print_type_identifier(out, snt.text, ct->type_hash);
723 : 42 : fprintf(out->fp,
724 : : "static inline size_t %s_vec_len(%s_vec_t vec)\n"
725 : : "__%svec_len(vec)\n",
726 : : snt.text, snt.text, nsc);
727 : 42 : fprintf(out->fp,
728 : : "__%sstruct_as_root(%s)\n",
729 : : nsc, snt.text);
730 : 42 : fprintf(out->fp, "\n");
731 : :
732 : : /* Create accessors which respect endianness and which return 0 on null struct access. */
733 [ + + ]: 116 : for (sym = ct->members; sym; sym = sym->link) {
734 : : member = (fb_member_t *)sym;
735 [ + + ]: 74 : if (member->metadata_flags & fb_f_deprecated) {
736 : 1 : continue;
737 : : }
738 : : symbol_name(&member->symbol, &n, &s);
739 [ + + - ]: 73 : switch (member->type.type) {
740 : : case vt_scalar_type:
741 : 53 : tname_ns = scalar_type_ns(member->type.st, nsc);
742 : 53 : tname = scalar_type_name(member->type.st);
743 : 53 : tname_prefix = scalar_type_prefix(member->type.st);
744 : 53 : fprintf(out->fp,
745 : : "static inline %s%s %s_%.*s(%s_struct_t t)\n"
746 : : "__%sstruct_scalar_field(t, %.*s, %s%s)\n",
747 : : tname_ns, tname, snt.text, n, s, snt.text,
748 : : nsc, n, s, nsc, tname_prefix);
749 [ + + ]: 53 : if (member->metadata_flags & fb_f_key) {
750 [ - + ]: 2 : if (already_has_key) {
751 : 0 : fprintf(out->fp, "/* Note: this is not the first field with a key on this struct. */\n");
752 : : }
753 : 2 : fprintf(out->fp, "/* Note: find only works on vectors sorted by this field. */\n");
754 : 2 : fprintf(out->fp,
755 : : "__%sdefine_find_by_scalar_field(%s, %.*s, %s%s)\n",
756 : : nsc, snt.text, n, s, tname_ns, tname);
757 [ + - ]: 2 : if (out->opts->cgen_sort) {
758 : 2 : fprintf(out->fp,
759 : : "__%sdefine_sort_by_scalar_field(%s, %.*s, %s%s, %s_t)\n",
760 : : nsc, snt.text, n, s, tname_ns, tname, snt.text);
761 : : }
762 [ + - ]: 2 : if (!already_has_key) {
763 : 2 : fprintf(out->fp,
764 : : "#define %s_vec_find %s_vec_find_by_%.*s\n",
765 : : snt.text, snt.text, n, s);
766 [ + - ]: 2 : if (out->opts->cgen_sort) {
767 : 2 : fprintf(out->fp,
768 : : "#define %s_vec_sort %s_vec_sort_by_%.*s\n",
769 : : snt.text, snt.text, n, s);
770 : : }
771 : : already_has_key = 1;
772 : : }
773 : : current_key_processed = 1;
774 : : }
775 : : break;
776 : : case vt_compound_type_ref:
777 : 20 : fb_compound_name(member->type.ct, &snref);
778 [ + + - ]: 20 : switch (member->type.ct->symbol.kind) {
779 : : case fb_is_enum:
780 : 10 : tname_prefix = scalar_type_prefix(member->type.ct->type.st);
781 : 10 : fprintf(out->fp,
782 : : "static inline %s_enum_t %s_%.*s(%s_struct_t t)\n"
783 : : "__%sstruct_scalar_field(t, %.*s, %s%s)\n",
784 : : snref.text, snt.text, n, s, snt.text,
785 : : nsc, n, s, nsc, tname_prefix);
786 [ + + ]: 10 : if (member->metadata_flags & fb_f_key) {
787 [ - + ]: 1 : if (already_has_key) {
788 : 0 : fprintf(out->fp, "/* Note: this is not the first field with a key on this table. */\n");
789 : : }
790 : 1 : fprintf(out->fp, "/* Note: find only works on vectors sorted by this field. */\n");
791 : 1 : fprintf(out->fp,
792 : : "__%sdefine_find_by_scalar_field(%s, %.*s, %s_enum_t)\n",
793 : : nsc, snt.text, n, s, snref.text);
794 [ + - ]: 1 : if (out->opts->cgen_sort) {
795 : 1 : fprintf(out->fp,
796 : : "__%sdefine_sort_by_scalar_field(%s, %.*s, %s_enum_t, %s_t)\n",
797 : : nsc, snt.text, n, s, snref.text, snt.text);
798 : : }
799 [ + - ]: 1 : if (!already_has_key) {
800 : 1 : fprintf(out->fp,
801 : : "#define %s_vec_find %s_vec_find_by_%.*s\n",
802 : : snt.text, snt.text, n, s);
803 [ + - ]: 1 : if (out->opts->cgen_sort) {
804 : 1 : fprintf(out->fp,
805 : : "#define %s_vec_sort %s_vec_sort_by_%.*s\n",
806 : : snt.text, snt.text, n, s);
807 : : }
808 : : already_has_key = 1;
809 : : }
810 : : current_key_processed = 1;
811 : : }
812 : : break;
813 : : case fb_is_struct:
814 : : /*
815 : : * For completeness provide an accessor which returns member pointer
816 : : * or null if container struct is null.
817 : : */
818 : 10 : fprintf(out->fp,
819 : : "static inline %s_struct_t %s_%.*s(%s_struct_t t)\n"
820 : : "__%sstruct_struct_field(t, %.*s)\n",
821 : : snref.text, snt.text, n, s, snt.text,
822 : : nsc, n, s);
823 : 10 : break;
824 : : }
825 : : }
826 [ + + ][ - + ]: 73 : if ((member->metadata_flags & fb_f_key) && !current_key_processed) {
827 : 0 : fprintf(out->fp,
828 : : "/* Note: field has key, but there is no support for find by fields of this type. */\n");
829 : : /*
830 : : * If the first key already exists, but was for an unsupported
831 : : * type, we do not map the next possible key to generic find.
832 : : */
833 : : already_has_key = 1;
834 : : }
835 : 73 : fprintf(out->fp, "\n");
836 : : }
837 : 42 : }
838 : :
839 : : /*
840 : : * Enums are integers, but we cannot control the size.
841 : : * To produce a typesafe and portable result, we generate constants
842 : : * instead.
843 : : */
844 : 36 : static void gen_enum(fb_output_t *out, fb_compound_type_t *ct)
845 : : {
846 : : fb_member_t *member;
847 : : fb_symbol_t *sym;
848 : : const char *tname, *tname_ns, *suffix, *s, *kind;
849 : : int n, w;
850 : : int is_union;
851 : : fb_scoped_name_t snt;
852 : 36 : const char *nsc = out->nsc;
853 : :
854 : 36 : fb_clear(snt);
855 : :
856 : : assert(ct->symbol.kind == fb_is_enum || ct->symbol.kind == fb_is_union);
857 : : assert(ct->type.type == vt_scalar_type);
858 : :
859 : 36 : tname_ns = scalar_type_ns(ct->type.st, nsc);
860 : 36 : tname = scalar_type_name(ct->type.st);
861 : 36 : suffix = scalar_suffix(ct->type.st);
862 : :
863 : 36 : w = (int)ct->size * 8;
864 : :
865 : 36 : is_union = ct->symbol.kind != fb_is_enum;
866 [ + + ]: 36 : kind = is_union ? "union_type" : "enum";
867 : : fb_compound_name(ct, &snt);
868 : 36 : print_doc(out, "", ct->doc);
869 : 36 : fprintf(out->fp,
870 : : "typedef %s%s %s_%s_t;\n",
871 : : tname_ns, tname, snt.text, kind);
872 : 36 : fprintf(out->fp,
873 : : "__%sdefine_integer_type(%s, %s_%s_t, %u)\n",
874 : : nsc, snt.text, snt.text, kind, w);
875 [ + + ]: 129 : for (sym = ct->members; sym; sym = sym->link) {
876 : : member = (fb_member_t *)sym;
877 : 93 : print_doc(out, "", member->doc);
878 : : symbol_name(&member->symbol, &n, &s);
879 : : /*
880 : : * This must be a define, not a static const integer, otherwise it
881 : : * won't work in switch statements - except with GNU extensions.
882 : : */
883 [ + + + - ]: 93 : switch (member->value.type) {
884 : : case vt_uint:
885 : 34 : fprintf(out->fp,
886 : : "#define %s_%.*s ((%s_%s_t)%llu%s)\n",
887 : 34 : snt.text, n, s, snt.text, kind, llu(member->value.u), suffix);
888 : 34 : break;
889 : : case vt_int:
890 : 57 : fprintf(out->fp,
891 : : "#define %s_%.*s ((%s_%s_t)%lld%s)\n",
892 : 57 : snt.text, n, s, snt.text, kind, llu(member->value.i), suffix);
893 : 57 : break;
894 : : case vt_bool:
895 : 2 : fprintf(out->fp,
896 : : "#define %s_%.*s ((%s_%s_t)%u)\n",
897 : 2 : snt.text, n, s, snt.text, kind, member->value.b);
898 : 2 : break;
899 : : default:
900 : 0 : gen_panic(out, "internal error: unexpected value type in enum");
901 : : break;
902 : : }
903 : : }
904 : 36 : fprintf(out->fp, "\n");
905 : :
906 [ + + ]: 36 : if (is_union) {
907 : 9 : fprintf(out->fp, "static inline const char *%s_type_name(%s_union_type_t type)\n"
908 : : "{\n",
909 : : snt.text, snt.text);
910 : : } else {
911 : 27 : fprintf(out->fp, "static inline const char *%s_name(%s_enum_t value)\n"
912 : : "{\n",
913 : : snt.text, snt.text);
914 : : }
915 : :
916 [ + + ]: 36 : if (is_union) {
917 : 9 : fprintf(out->fp, " switch (type) {\n");
918 : : } else {
919 : 27 : fprintf(out->fp, " switch (value) {\n");
920 : : }
921 [ + + ]: 129 : for (sym = ct->members; sym; sym = sym->link) {
922 : : member = (fb_member_t *)sym;
923 : : symbol_name(&member->symbol, &n, &s);
924 [ - + ]: 93 : if (sym->flags & fb_duplicate) {
925 : 0 : fprintf(out->fp,
926 : : " /* case %s_%.*s: return \"%.*s\"; (duplicate) */\n",
927 : : snt.text, n, s, n, s);
928 : : } else {
929 : 93 : fprintf(out->fp,
930 : : " case %s_%.*s: return \"%.*s\";\n",
931 : : snt.text, n, s, n, s);
932 : : }
933 : : }
934 : 36 : fprintf(out->fp,
935 : : " default: return \"\";\n"
936 : : " }\n"
937 : : "}\n");
938 : 36 : fprintf(out->fp, "\n");
939 : 36 : }
940 : :
941 : 9 : static void gen_nested_root(fb_output_t *out, fb_symbol_t *root_type, fb_symbol_t *container, fb_symbol_t *member)
942 : : {
943 : : const char *s;
944 : : int n;
945 : : const char *kind;
946 : 9 : const char *nsc = out->nsc;
947 : : fb_scoped_name_t snt;
948 : : fb_scoped_name_t snc;
949 : :
950 : 9 : fb_clear(snt);
951 : 9 : fb_clear(snc);
952 [ + - ]: 9 : if (!root_type) {
953 : 0 : return;
954 : : }
955 : : /*
956 : : * Current flatc compiler only accepts tables, but here we support
957 : : * both tables and structs in so far the parser and analyzer
958 : : * allows for it.
959 : : */
960 [ + - + ]: 9 : switch (root_type->kind) {
961 : : case fb_is_table:
962 : : kind = "table_";
963 : : break;
964 : : case fb_is_struct:
965 : : kind = "struct_";
966 : 1 : break;
967 : : default:
968 : 0 : gen_panic(out, "internal error: roots can only be structs or tables");
969 : : return;
970 : : }
971 : : fb_compound_name((fb_compound_type_t *)root_type, &snt);
972 : : assert(container->kind == fb_is_table);
973 : : fb_compound_name((fb_compound_type_t *)container, &snc);
974 : : symbol_name(member, &n, &s);
975 : 9 : fprintf(out->fp, "__%snested_buffer_as_root(%s, %.*s, %s, %s)\n", nsc, snc.text, n, s, snt.text, kind);
976 : : }
977 : :
978 : 63 : static void gen_table(fb_output_t *out, fb_compound_type_t *ct)
979 : : {
980 : : fb_member_t *member;
981 : : fb_symbol_t *sym;
982 : : const char *s, *tname, *tname_ns, *tname_prefix;
983 : : int n, r;
984 : : int already_has_key, current_key_processed;
985 : 63 : const char *nsc = out->nsc;
986 : : fb_scoped_name_t snt;
987 : : fb_scoped_name_t snref;
988 : : uint64_t present_id;
989 : :
990 : : assert(ct->symbol.kind == fb_is_table);
991 : :
992 : 63 : fb_clear(snt);
993 : 63 : fb_clear(snref);
994 : :
995 : 63 : fprintf(out->fp, "\n");
996 : : fb_compound_name(ct, &snt);
997 : 63 : print_doc(out, "", ct->doc);
998 : 63 : fprintf(out->fp,
999 : : /*
1000 : : * We don't really need the struct, but it provides better
1001 : : * type safety than a typedef void *.
1002 : : */
1003 : : "struct %s_table { uint8_t unused__; };\n"
1004 : : "\n",
1005 : : snt.text);
1006 : 63 : print_type_identifier(out, snt.text, ct->type_hash);
1007 : 63 : fprintf(out->fp,
1008 : : "static inline size_t %s_vec_len(%s_vec_t vec)\n"
1009 : : "__%svec_len(vec)\n",
1010 : : snt.text, snt.text, nsc);
1011 : 63 : fprintf(out->fp,
1012 : : "static inline %s_table_t %s_vec_at(%s_vec_t vec, size_t i)\n"
1013 : : "__%soffset_vec_at(%s_table_t, vec, i, 0)\n",
1014 : : snt.text, snt.text, snt.text, nsc, snt.text);
1015 : 63 : fprintf(out->fp,
1016 : : "__%stable_as_root(%s)\n",
1017 : : nsc, snt.text);
1018 : 63 : fprintf(out->fp, "\n");
1019 : :
1020 : : already_has_key = 0;
1021 [ + + ]: 408 : for (sym = ct->members; sym; sym = sym->link) {
1022 : : current_key_processed = 0;
1023 : : member = (fb_member_t *)sym;
1024 : 345 : present_id = member->id;
1025 : 345 : print_doc(out, "", member->doc);
1026 : : /*
1027 : : * In flatc, there can at most one key field, and it should be
1028 : : * scalar or string. Here we export all keys using the
1029 : : * <table>_vec_find_by_<fieldname> convention and let the parser deal with
1030 : : * semantics. Keys on unsupported fields are ignored. The first
1031 : : * valid find operation is also mapped to just <table>_vec_find.
1032 : : */
1033 : : symbol_name(&member->symbol, &n, &s);
1034 [ + + ]: 345 : if (member->metadata_flags & fb_f_deprecated) {
1035 : 9 : fprintf(out->fp, "/* Skipping deprecated field: '%s_%.*s' */\n\n", snt.text, n, s);
1036 : 9 : continue;
1037 : : }
1038 : 336 : r = (member->metadata_flags & fb_f_required) != 0;
1039 [ + + + + : 336 : switch (member->type.type) {
+ + - ]
1040 : : case vt_scalar_type:
1041 : 147 : tname_ns = scalar_type_ns(member->type.st, nsc);
1042 : 147 : tname = scalar_type_name(member->type.st);
1043 : 147 : tname_prefix = scalar_type_prefix(member->type.st);
1044 [ + + + + : 147 : switch (member->value.type) {
- ]
1045 : : case vt_uint:
1046 : 65 : fprintf(out->fp,
1047 : : "static inline %s%s %s_%.*s(%s_table_t t)\n"
1048 : : "__%sscalar_field(%s%s, %llu, %llu, t)\n",
1049 : : tname_ns, tname, snt.text, n, s, snt.text,
1050 : 65 : nsc, nsc, tname_prefix, llu(member->id), llu(member->value.u));
1051 : 65 : break;
1052 : : case vt_int:
1053 : 75 : fprintf(out->fp,
1054 : : "static inline %s%s %s_%.*s(%s_table_t t)\n"
1055 : : "__%sscalar_field(%s%s, %llu, %lld, t)\n",
1056 : : tname_ns, tname, snt.text, n, s, snt.text,
1057 : 75 : nsc, nsc, tname_prefix, llu(member->id), lld(member->value.i));
1058 : 75 : break;
1059 : : case vt_bool:
1060 : 2 : fprintf(out->fp,
1061 : : "static inline %s%s %s_%.*s(%s_table_t t)\n"
1062 : : "__%sscalar_field(%s%s, %llu, %u, t)\n",
1063 : : tname_ns, tname, snt.text, n, s, snt.text,
1064 : 4 : nsc, nsc, tname_prefix, llu(member->id), member->value.b);
1065 : 2 : break;
1066 : : case vt_float:
1067 : 5 : fprintf(out->fp,
1068 : : "static inline %s%s %s_%.*s(%s_table_t t)\n"
1069 : : "__%sscalar_field(%s%s, %llu, %lf, t)\n",
1070 : : tname_ns, tname, snt.text, n, s, snt.text,
1071 : 5 : nsc, nsc, tname_prefix, llu(member->id), member->value.f);
1072 : 5 : break;
1073 : : default:
1074 : 0 : gen_panic(out, "internal error: unexpected scalar table default value");
1075 : : continue;
1076 : : }
1077 [ + + ]: 147 : if (member->metadata_flags & fb_f_key) {
1078 [ + + ]: 2 : if (already_has_key) {
1079 : 1 : fprintf(out->fp, "/* Note: this is not the first field with a key on this table. */\n");
1080 : : }
1081 : 2 : fprintf(out->fp, "/* Note: find only works on vectors sorted by this field. */\n");
1082 : 2 : fprintf(out->fp,
1083 : : "__%sdefine_find_by_scalar_field(%s, %.*s, %s%s)\n",
1084 : : nsc, snt.text, n, s, tname_ns, tname);
1085 [ + - ]: 2 : if (out->opts->cgen_sort) {
1086 : 2 : fprintf(out->fp,
1087 : : "__%sdefine_sort_by_scalar_field(%s, %.*s, %s%s, %suoffset_t)\n",
1088 : : nsc, snt.text, n, s, tname_ns, tname, nsc);
1089 : : }
1090 [ + + ]: 2 : if (!already_has_key) {
1091 : 1 : fprintf(out->fp,
1092 : : "#define %s_vec_find %s_vec_find_by_%.*s\n",
1093 : : snt.text, snt.text, n, s);
1094 [ + - ]: 1 : if (out->opts->cgen_sort) {
1095 : 1 : fprintf(out->fp,
1096 : : "#define %s_vec_sort %s_vec_sort_by_%.*s\n",
1097 : : snt.text, snt.text, n, s);
1098 : : }
1099 : : already_has_key = 1;
1100 : : }
1101 : : current_key_processed = 1;
1102 : : }
1103 : : break;
1104 : : case vt_vector_type:
1105 : : /* They all use a namespace. */
1106 : 25 : tname = scalar_vector_type_name(member->type.st);
1107 : : tname_ns = nsc;
1108 : 25 : fprintf(out->fp,
1109 : : "static inline %s%s %s_%.*s(%s_table_t t)\n"
1110 : : "__%svector_field(%s%s, %llu, t, %u)\n",
1111 : : tname_ns, tname, snt.text, n, s, snt.text,
1112 : 25 : nsc, tname_ns, tname, llu(member->id), r);
1113 [ + + ]: 25 : if (member->nest) {
1114 : 9 : gen_nested_root(out, &member->nest->symbol, &ct->symbol, &member->symbol);
1115 : : }
1116 : : break;
1117 : : case vt_string_type:
1118 : 27 : fprintf(out->fp,
1119 : : "static inline %sstring_t %s_%.*s(%s_table_t t)\n"
1120 : : "__%svector_field(%sstring_t, %llu, t, %u)\n",
1121 : : nsc, snt.text, n, s, snt.text,
1122 : 27 : nsc, nsc, llu(member->id), r);
1123 [ + + ]: 27 : if (member->metadata_flags & fb_f_key) {
1124 [ - + ]: 8 : if (already_has_key) {
1125 : 0 : fprintf(out->fp, "/* Note: this is not the first field with a key on this table. */\n");
1126 : : }
1127 : 8 : fprintf(out->fp, "/* Note: find only works on vectors sorted by this field. */\n");
1128 : 8 : fprintf(out->fp,
1129 : : "static inline size_t %s_vec_find_by_%.*s(%s_vec_t vec, const char *s)\n"
1130 : : "__%sfind_by_string_field(%s_%.*s, vec, %s_vec_at, %s_vec_len, s)\n",
1131 : : snt.text, n, s, snt.text, nsc, snt.text, n, s, snt.text, snt.text);
1132 : 8 : fprintf(out->fp,
1133 : : "static inline size_t %s_vec_find_n_by_%.*s(%s_vec_t vec, const char *s, int n)\n"
1134 : : "__%sfind_by_string_n_field(%s_%.*s, vec, %s_vec_at, %s_vec_len, s, n)\n",
1135 : : snt.text, n, s, snt.text, nsc, snt.text, n, s, snt.text, snt.text);
1136 [ + - ]: 8 : if (out->opts->cgen_sort) {
1137 : 8 : fprintf(out->fp,
1138 : : "__%sdefine_sort_by_string_field(%s, %.*s)\n",
1139 : : nsc, snt.text, n, s);
1140 : : }
1141 [ + - ]: 8 : if (!already_has_key) {
1142 : 8 : fprintf(out->fp,
1143 : : "#define %s_vec_find %s_vec_find_by_%.*s\n"
1144 : : "#define %s_vec_find_n %s_vec_find_n_by_%.*s\n",
1145 : : snt.text, snt.text, n, s,
1146 : : snt.text, snt.text, n, s);
1147 [ + - ]: 8 : if (out->opts->cgen_sort) {
1148 : 8 : fprintf(out->fp,
1149 : : "#define %s_vec_sort %s_vec_sort_by_%.*s\n",
1150 : : snt.text, snt.text, n, s);
1151 : : }
1152 : : already_has_key = 1;
1153 : : }
1154 : : current_key_processed = 1;
1155 : : }
1156 : : break;
1157 : : case vt_vector_string_type:
1158 : 10 : fprintf(out->fp,
1159 : : "static inline %sstring_vec_t %s_%.*s(%s_table_t t)\n"
1160 : : "__%svector_field(%sstring_vec_t, %llu, t, %u)\n",
1161 : : nsc, snt.text, n, s, snt.text,
1162 : 10 : nsc, nsc, llu(member->id), r);
1163 : 10 : break;
1164 : : case vt_compound_type_ref:
1165 : 109 : fb_compound_name(member->type.ct, &snref);
1166 [ + + + + : 109 : switch (member->type.ct->symbol.kind) {
- ]
1167 : : case fb_is_struct:
1168 : 27 : fprintf(out->fp,
1169 : : "static inline %s_struct_t %s_%.*s(%s_table_t t)\n"
1170 : : "__%sstruct_field(%s_struct_t, %llu, t, %u)\n",
1171 : : snref.text, snt.text, n, s, snt.text,
1172 : 27 : nsc, snref.text, llu(member->id), r);
1173 : 27 : break;
1174 : : case fb_is_table:
1175 : 23 : fprintf(out->fp,
1176 : : "static inline %s_table_t %s_%.*s(%s_table_t t)\n"
1177 : : "__%stable_field(%s_table_t, %llu, t, %u)\n",
1178 : : snref.text, snt.text, n, s, snt.text,
1179 : 23 : nsc, snref.text, llu(member->id), r);
1180 : 23 : break;
1181 : : case fb_is_enum:
1182 [ + + - - ]: 48 : switch (member->value.type) {
1183 : : case vt_uint:
1184 : 2 : fprintf(out->fp,
1185 : : "static inline %s_enum_t %s_%.*s(%s_table_t t)\n"
1186 : : "__%sscalar_field(%s, %llu, %llu, t)\n",
1187 : : snref.text, snt.text, n, s, snt.text,
1188 : 2 : nsc, snref.text, llu(member->id), llu(member->value.u));
1189 : 2 : break;
1190 : : case vt_int:
1191 : 46 : fprintf(out->fp,
1192 : : "static inline %s_enum_t %s_%.*s(%s_table_t t)\n"
1193 : : "__%sscalar_field(%s, %llu, %lld, t)\n",
1194 : : snref.text, snt.text, n, s, snt.text,
1195 : 46 : nsc, snref.text, llu(member->id), lld(member->value.i));
1196 : 46 : break;
1197 : : case vt_bool:
1198 : 0 : fprintf(out->fp,
1199 : : "static inline %s_enum_t %s_%.*s(%s_table_t t)\n"
1200 : : "__%sscalar_field(%s, %llu, %u, t)\n",
1201 : : snref.text, snt.text, n, s, snt.text,
1202 : 0 : nsc, snref.text, llu(member->id), member->value.b);
1203 : 0 : break;
1204 : : default:
1205 : 0 : gen_panic(out, "internal error: unexpected enum type referenced by table");
1206 : : continue;
1207 : : }
1208 [ + + ]: 48 : if (member->metadata_flags & fb_f_key) {
1209 [ - + ]: 1 : if (already_has_key) {
1210 : 0 : fprintf(out->fp, "/* Note: this is not the first field with a key on this table. */\n");
1211 : : }
1212 : 1 : fprintf(out->fp, "/* Note: find only works on vectors sorted by this field. */\n");
1213 : 1 : fprintf(out->fp,
1214 : : "__%sdefine_find_by_scalar_field(%s, %.*s, %s_enum_t)\n",
1215 : : nsc, snt.text, n, s, snref.text);
1216 [ + - ]: 1 : if (out->opts->cgen_sort) {
1217 : 1 : fprintf(out->fp,
1218 : : "__%sdefine_sort_by_scalar_field(%s, %.*s, %s_enum_t, %suoffset_t)\n",
1219 : : nsc, snt.text, n, s, snref.text, nsc);
1220 : : }
1221 [ + - ]: 1 : if (!already_has_key) {
1222 : 1 : fprintf(out->fp,
1223 : : "#define %s_vec_find %s_vec_find_by_%.*s\n",
1224 : : snt.text, snt.text, n, s);
1225 [ + - ]: 1 : if (out->opts->cgen_sort) {
1226 : 1 : fprintf(out->fp,
1227 : : "#define %s_vec_sort %s_vec_sort_by_%.*s\n",
1228 : : snt.text, snt.text, n, s);
1229 : : }
1230 : : already_has_key = 1;
1231 : : }
1232 : : current_key_processed = 1;
1233 : : }
1234 : : break;
1235 : : case fb_is_union:
1236 : 11 : present_id--;
1237 : 11 : fprintf(out->fp,
1238 : : "static inline %s_union_type_t %s_%.*s_type(%s_table_t t)\n"
1239 : : "__%sscalar_field(%s, %llu, 0, t)\n",
1240 : : snref.text, snt.text, n, s, snt.text,
1241 : 11 : nsc, snref.text, llu(member->id) - 1);
1242 : 11 : fprintf(out->fp,
1243 : : "static inline %sgeneric_table_t %s_%.*s(%s_table_t t)\n"
1244 : : "__%stable_field(%sgeneric_table_t, %llu, t, %u)\n",
1245 : : nsc, snt.text, n, s, snt.text,
1246 : 11 : nsc, nsc, llu(member->id), r);
1247 : 11 : break;
1248 : : default:
1249 : 0 : gen_panic(out, "internal error: unexpected compound type in table during code generation");
1250 : : break;
1251 : : }
1252 : : break;
1253 : : case vt_vector_compound_type_ref:
1254 : 18 : fb_compound_name(member->type.ct, &snref);
1255 [ + + + - : 18 : switch (member->type.ct->symbol.kind) {
- ]
1256 : : case fb_is_struct:
1257 : 8 : fprintf(out->fp,
1258 : : "static inline %s_vec_t %s_%.*s(%s_table_t t)\n"
1259 : : "__%svector_field(%s_vec_t, %llu, t, %u)\n",
1260 : : snref.text, snt.text, n, s, snt.text,
1261 : 8 : nsc, snref.text, llu(member->id), r);
1262 : 8 : break;
1263 : : case fb_is_table:
1264 : 9 : fprintf(out->fp,
1265 : : "static inline %s_vec_t %s_%.*s(%s_table_t t)\n"
1266 : : "__%svector_field(%s_vec_t, %llu, t, %u)\n",
1267 : : snref.text, snt.text, n, s, snt.text,
1268 : 9 : nsc, snref.text, llu(member->id), r);
1269 : 9 : break;
1270 : : case fb_is_enum:
1271 : 1 : fprintf(out->fp,
1272 : : "static inline %s_vec_t %s_%.*s(%s_table_t t)\n"
1273 : : "__%svector_field(%s_vec_t, %llu, t, %u)\n",
1274 : : snref.text, snt.text, n, s, snt.text,
1275 : 1 : nsc, snref.text, llu(member->id), r);
1276 : 1 : break;
1277 : : case fb_is_union:
1278 : 0 : gen_panic(out, "internal error: unexpected vector of union present in table");
1279 : : break;
1280 : : default:
1281 : 0 : gen_panic(out, "internal error: unexpected vector compound type in table during code generation");
1282 : : break;
1283 : : }
1284 : : break;
1285 : : default:
1286 : 0 : gen_panic(out, "internal error: unexpected table member type during code generation");
1287 : : break;
1288 : : }
1289 : 336 : fprintf(out->fp,
1290 : : "static inline int %s_%.*s_is_present(%s_table_t t)\n"
1291 : : "__%sfield_present(%llu, t)\n",
1292 : : snt.text, n, s, snt.text, nsc, llu(present_id));
1293 [ + + ][ - + ]: 336 : if ((member->metadata_flags & fb_f_key) && !current_key_processed) {
1294 : 0 : fprintf(out->fp,
1295 : : "/* Note: field has key, but there is no support for find by fields of this type. */\n");
1296 : : /*
1297 : : * If the first key already exists, but was for an unsupported
1298 : : * type, we do not map the next possible key to generic find.
1299 : : */
1300 : : already_has_key = 1;
1301 : : }
1302 : 336 : fprintf(out->fp, "\n");
1303 : : }
1304 : 63 : }
1305 : :
1306 : 24 : int fb_gen_c_reader(fb_output_t *out)
1307 : : {
1308 : : fb_symbol_t *sym;
1309 : : fb_compound_type_t *ct;
1310 : :
1311 : 24 : gen_pretext(out);
1312 : :
1313 [ + + ]: 66 : for (ct = out->S->ordered_structs; ct; ct = ct->order) {
1314 : 42 : gen_forward_decl(out, ct);
1315 : : }
1316 : 24 : fprintf(out->fp, "\n");
1317 [ + + ]: 166 : for (sym = out->S->symbols; sym; sym = sym->link) {
1318 [ + + ]: 142 : switch (sym->kind) {
1319 : : case fb_is_table:
1320 : 63 : gen_forward_decl(out, (fb_compound_type_t *)sym);
1321 : 63 : break;
1322 : : }
1323 : : }
1324 : 24 : fprintf(out->fp, "\n");
1325 [ + + ]: 166 : for (sym = out->S->symbols; sym; sym = sym->link) {
1326 [ + + ]: 142 : switch (sym->kind) {
1327 : : /* Enums must come before structs in case they are referenced. */
1328 : : case fb_is_enum:
1329 : 27 : gen_enum(out, (fb_compound_type_t *)sym);
1330 : 27 : break;
1331 : : }
1332 : : }
1333 : 24 : fprintf(out->fp, "\n");
1334 : : /* Generate structs in topologically sorted order. */
1335 [ + + ]: 66 : for (ct = out->S->ordered_structs; ct; ct = ct->order) {
1336 : 42 : gen_struct(out, ct);
1337 : : }
1338 [ + + ]: 166 : for (sym = out->S->symbols; sym; sym = sym->link) {
1339 [ + + - + ]: 142 : switch (sym->kind) {
1340 : : case fb_is_enum:
1341 : : case fb_is_struct:
1342 : : /* Already generated. */
1343 : : break;
1344 : : case fb_is_union:
1345 : 9 : gen_enum(out, (fb_compound_type_t *)sym);
1346 : 9 : break;
1347 : : case fb_is_table:
1348 : 63 : gen_table(out, (fb_compound_type_t *)sym);
1349 : 63 : break;
1350 : : case fb_is_rpc_service:
1351 : : /* Ignore. */
1352 : : break;
1353 : : default:
1354 : 0 : gen_panic(out, "internal error: unexpected schema component");
1355 : : break;
1356 : : }
1357 : : }
1358 : 24 : fprintf(out->fp, "\n");
1359 : 24 : gen_footer(out);
1360 : 24 : return 0;
1361 : : }
|