v2.3.0 — tables: declare your own file formats
What you get
- Tables — declare your own file formats. A
tabledeclaration is a data record for content that outlives builds: config files, asset archives, tool output. Declare a root table, nest tables by value, and the generated C++ saves and loads it — no envelope imposed, no document concept, no prescribed structure. Your root table IS your format. - Version tolerance built into the wire. Add, remove, and reorder fields freely; old readers skip what they cannot name, new readers default what old writers never wrote, changed kinds are skipped and counted, out-of-range values clamp and count — nothing crashes on data from a different build, in either direction, and every event lands in a read report. Renames keep identity with
was = "old_name", and id collisions are refused at compile time. - Reflection for tools. Generated descriptors carry every field's name, kind, offset, bounds and nesting — walk properties by name, print, diff, or bind an editor with no RTTI and no schema files at runtime.
- Relocatable, and built wide. Generated table structs are trivially copyable (statically asserted); encode splits into measure and save over caller-provided buffers, and nested tables are length-prefixed — so builds can measure in parallel, prefix-sum, and scatter-write from many threads, and saves into exactly-measured buffers succeed byte for byte.
- Self-contained headers. Generated
<Unit>Table.hhas no dependency on the serialize runtime — includable from any TU. C++ today; every other backend refuses table units by name. - Dart fixes. Fixed-point field defaults are no longer double-scaled, and declarations named after
dart:coretypes (List,String, …) are refused at generation instead of emitting shadowing classes.
SPEC-TABLES.md specifies the full wire — neutral little-endian TLV a third party can implement from the document alone. The packet wire (type, the protocol id) is untouched by all of this: tables and packets version independently by construction.